diff --git a/.bazelversion b/.bazelversion index 5e3254243a3..dc0208aba8e 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.1.2 +6.3.1 diff --git a/config/identical-files.json b/config/identical-files.json index ceed02ba5d6..19103323a23 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -1,24 +1,4 @@ { - "DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [ - "java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll", - "cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll", - "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll", - "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll", - "go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll", - "swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll" - ], - "DataFlowImpl Java/C++/C#/Go/Python/Ruby/Swift": [ - "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll", - "cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll", - "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll", - "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll", - "go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll", - "swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll" - ], "DataFlow Java/C++/C#/Go/Python/Ruby/Swift Legacy Configuration": [ "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll", "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll", @@ -53,16 +33,6 @@ "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll", "swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll" ], - "DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [ - "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll", - "cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll", - "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll", - "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll", - "go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll", - "swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll" - ], "TaintTracking Java/C++/C#/Go/Python/Ruby/Swift": [ "cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll", "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll", @@ -516,7 +486,6 @@ ], "CFG": [ "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", - "ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll", "swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll" ], "TypeTracker": [ @@ -603,4 +572,4 @@ "python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll", "java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll" ] -} +} \ No newline at end of file diff --git a/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/old.dbscheme b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/old.dbscheme new file mode 100644 index 00000000000..d77c09d8bdc --- /dev/null +++ b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/old.dbscheme @@ -0,0 +1,2212 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +case @macroinvocation.kind of + 1 = @macro_expansion +| 2 = @other_macro_reference +; + +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* +case @function.kind of + 1 = @normal_function +| 2 = @constructor +| 3 = @destructor +| 4 = @conversion_function +| 5 = @operator +| 6 = @builtin_function // GCC built-in functions, e.g. __builtin___memcpy_chk +; +*/ + +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point( + int id: @function ref, + unique int entry_point: @stmt ref +); + +function_return_type( + int id: @function ref, + int return_type: @type ref +); + +/** + * If `function` is a coroutine, then this gives the `std::experimental::resumable_traits` + * instance associated with it, and the variables representing the `handle` and `promise` + * for it. + */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type( + unique int id: @function ref, + int this_type: @type ref +); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides( + int new: @function ref, + int old: @function ref +); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +orphaned_variables( + int var: @localvariable ref, + int function: @function ref +) + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/** + * Built-in types are the fundamental types, e.g., integral, floating, and void. + */ +case @builtintype.kind of + 1 = @errortype +| 2 = @unknowntype +| 3 = @void +| 4 = @boolean +| 5 = @char +| 6 = @unsigned_char +| 7 = @signed_char +| 8 = @short +| 9 = @unsigned_short +| 10 = @signed_short +| 11 = @int +| 12 = @unsigned_int +| 13 = @signed_int +| 14 = @long +| 15 = @unsigned_long +| 16 = @signed_long +| 17 = @long_long +| 18 = @unsigned_long_long +| 19 = @signed_long_long +// ... 20 Microsoft-specific __int8 +// ... 21 Microsoft-specific __int16 +// ... 22 Microsoft-specific __int32 +// ... 23 Microsoft-specific __int64 +| 24 = @float +| 25 = @double +| 26 = @long_double +| 27 = @complex_float // C99-specific _Complex float +| 28 = @complex_double // C99-specific _Complex double +| 29 = @complex_long_double // C99-specific _Complex long double +| 30 = @imaginary_float // C99-specific _Imaginary float +| 31 = @imaginary_double // C99-specific _Imaginary double +| 32 = @imaginary_long_double // C99-specific _Imaginary long double +| 33 = @wchar_t // Microsoft-specific +| 34 = @decltype_nullptr // C++11 +| 35 = @int128 // __int128 +| 36 = @unsigned_int128 // unsigned __int128 +| 37 = @signed_int128 // signed __int128 +| 38 = @float128 // __float128 +| 39 = @complex_float128 // _Complex __float128 +| 40 = @decimal32 // _Decimal32 +| 41 = @decimal64 // _Decimal64 +| 42 = @decimal128 // _Decimal128 +| 43 = @char16_t +| 44 = @char32_t +| 45 = @std_float32 // _Float32 +| 46 = @float32x // _Float32x +| 47 = @std_float64 // _Float64 +| 48 = @float64x // _Float64x +| 49 = @std_float128 // _Float128 +// ... 50 _Float128x +| 51 = @char8_t +| 52 = @float16 // _Float16 +| 53 = @complex_float16 // _Complex _Float16 +; + +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/** + * Derived types are types that are directly derived from existing types and + * point to, refer to, transform type data to return a new type. + */ +case @derivedtype.kind of + 1 = @pointer +| 2 = @reference +| 3 = @type_with_specifiers +| 4 = @array +| 5 = @gnu_vector +| 6 = @routineptr +| 7 = @routinereference +| 8 = @rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated +| 10 = @block +; + +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* +case @usertype.kind of + 1 = @struct +| 2 = @class +| 3 = @union +| 4 = @enum +| 5 = @typedef // classic C: typedef typedef type name +| 6 = @template +| 7 = @template_parameter +| 8 = @template_template_parameter +| 9 = @proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated +| 13 = @scoped_enum +| 14 = @using_alias // a using name = type style typedef +; +*/ + +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the frontend. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +| 4 = @attribute_arg_constant_expr +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_constant( + unique int arg: @attribute_arg ref, + int constant: @expr ref +) +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall( + unique int caller: @funbindexpr ref, + int kind: int ref +); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // frontend internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +| 335 = @blockassignexpr +| 336 = @issame +| 337 = @isfunction +| 338 = @islayoutcompatible +| 339 = @ispointerinterconvertiblebaseof +| 340 = @isarray +| 341 = @arrayrank +| 342 = @arrayextent +| 343 = @isarithmetic +| 344 = @iscompletetype +| 345 = @iscompound +| 346 = @isconst +| 347 = @isfloatingpoint +| 348 = @isfundamental +| 349 = @isintegral +| 350 = @islvaluereference +| 351 = @ismemberfunctionpointer +| 352 = @ismemberobjectpointer +| 353 = @ismemberpointer +| 354 = @isobject +| 355 = @ispointer +| 356 = @isreference +| 357 = @isrvaluereference +| 358 = @isscalar +| 359 = @issigned +| 360 = @isunsigned +| 361 = @isvoid +| 362 = @isvolatile +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + | @issame + | @isfunction + | @islayoutcompatible + | @ispointerinterconvertiblebaseof + | @isarray + | @arrayrank + | @arrayextent + | @isarithmetic + | @iscompletetype + | @iscompound + | @isconst + | @isfloatingpoint + | @isfundamental + | @isintegral + | @islvaluereference + | @ismemberfunctionpointer + | @ismemberobjectpointer + | @ismemberpointer + | @isobject + | @ispointer + | @isreference + | @isrvaluereference + | @isscalar + | @issigned + | @isunsigned + | @isvoid + | @isvolatile + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref, + int position: int ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref, + int position: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/semmlecode.cpp.dbscheme b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..19887dbd333 --- /dev/null +++ b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/semmlecode.cpp.dbscheme @@ -0,0 +1,2212 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +case @macroinvocation.kind of + 1 = @macro_expansion +| 2 = @other_macro_reference +; + +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* +case @function.kind of + 1 = @normal_function +| 2 = @constructor +| 3 = @destructor +| 4 = @conversion_function +| 5 = @operator +| 6 = @builtin_function // GCC built-in functions, e.g. __builtin___memcpy_chk +; +*/ + +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point( + int id: @function ref, + unique int entry_point: @stmt ref +); + +function_return_type( + int id: @function ref, + int return_type: @type ref +); + +/** + * If `function` is a coroutine, then this gives the `std::experimental::resumable_traits` + * instance associated with it, and the variables representing the `handle` and `promise` + * for it. + */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type( + unique int id: @function ref, + int this_type: @type ref +); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides( + int new: @function ref, + int old: @function ref +); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +orphaned_variables( + int var: @localvariable ref, + int function: @function ref +) + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/** + * Built-in types are the fundamental types, e.g., integral, floating, and void. + */ +case @builtintype.kind of + 1 = @errortype +| 2 = @unknowntype +| 3 = @void +| 4 = @boolean +| 5 = @char +| 6 = @unsigned_char +| 7 = @signed_char +| 8 = @short +| 9 = @unsigned_short +| 10 = @signed_short +| 11 = @int +| 12 = @unsigned_int +| 13 = @signed_int +| 14 = @long +| 15 = @unsigned_long +| 16 = @signed_long +| 17 = @long_long +| 18 = @unsigned_long_long +| 19 = @signed_long_long +// ... 20 Microsoft-specific __int8 +// ... 21 Microsoft-specific __int16 +// ... 22 Microsoft-specific __int32 +// ... 23 Microsoft-specific __int64 +| 24 = @float +| 25 = @double +| 26 = @long_double +| 27 = @complex_float // C99-specific _Complex float +| 28 = @complex_double // C99-specific _Complex double +| 29 = @complex_long_double // C99-specific _Complex long double +| 30 = @imaginary_float // C99-specific _Imaginary float +| 31 = @imaginary_double // C99-specific _Imaginary double +| 32 = @imaginary_long_double // C99-specific _Imaginary long double +| 33 = @wchar_t // Microsoft-specific +| 34 = @decltype_nullptr // C++11 +| 35 = @int128 // __int128 +| 36 = @unsigned_int128 // unsigned __int128 +| 37 = @signed_int128 // signed __int128 +| 38 = @float128 // __float128 +| 39 = @complex_float128 // _Complex __float128 +| 40 = @decimal32 // _Decimal32 +| 41 = @decimal64 // _Decimal64 +| 42 = @decimal128 // _Decimal128 +| 43 = @char16_t +| 44 = @char32_t +| 45 = @std_float32 // _Float32 +| 46 = @float32x // _Float32x +| 47 = @std_float64 // _Float64 +| 48 = @float64x // _Float64x +| 49 = @std_float128 // _Float128 +| 50 = @float128x // _Float128x +| 51 = @char8_t +| 52 = @float16 // _Float16 +| 53 = @complex_float16 // _Complex _Float16 +; + +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/** + * Derived types are types that are directly derived from existing types and + * point to, refer to, transform type data to return a new type. + */ +case @derivedtype.kind of + 1 = @pointer +| 2 = @reference +| 3 = @type_with_specifiers +| 4 = @array +| 5 = @gnu_vector +| 6 = @routineptr +| 7 = @routinereference +| 8 = @rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated +| 10 = @block +; + +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* +case @usertype.kind of + 1 = @struct +| 2 = @class +| 3 = @union +| 4 = @enum +| 5 = @typedef // classic C: typedef typedef type name +| 6 = @template +| 7 = @template_parameter +| 8 = @template_template_parameter +| 9 = @proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated +| 13 = @scoped_enum +| 14 = @using_alias // a using name = type style typedef +; +*/ + +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the frontend. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +| 4 = @attribute_arg_constant_expr +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_constant( + unique int arg: @attribute_arg ref, + int constant: @expr ref +) +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall( + unique int caller: @funbindexpr ref, + int kind: int ref +); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // frontend internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +| 335 = @blockassignexpr +| 336 = @issame +| 337 = @isfunction +| 338 = @islayoutcompatible +| 339 = @ispointerinterconvertiblebaseof +| 340 = @isarray +| 341 = @arrayrank +| 342 = @arrayextent +| 343 = @isarithmetic +| 344 = @iscompletetype +| 345 = @iscompound +| 346 = @isconst +| 347 = @isfloatingpoint +| 348 = @isfundamental +| 349 = @isintegral +| 350 = @islvaluereference +| 351 = @ismemberfunctionpointer +| 352 = @ismemberobjectpointer +| 353 = @ismemberpointer +| 354 = @isobject +| 355 = @ispointer +| 356 = @isreference +| 357 = @isrvaluereference +| 358 = @isscalar +| 359 = @issigned +| 360 = @isunsigned +| 361 = @isvoid +| 362 = @isvolatile +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + | @issame + | @isfunction + | @islayoutcompatible + | @ispointerinterconvertiblebaseof + | @isarray + | @arrayrank + | @arrayextent + | @isarithmetic + | @iscompletetype + | @iscompound + | @isconst + | @isfloatingpoint + | @isfundamental + | @isintegral + | @islvaluereference + | @ismemberfunctionpointer + | @ismemberobjectpointer + | @ismemberpointer + | @isobject + | @ispointer + | @isreference + | @isrvaluereference + | @isscalar + | @issigned + | @isunsigned + | @isvoid + | @isvolatile + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref, + int position: int ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref, + int position: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/upgrade.properties b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/upgrade.properties new file mode 100644 index 00000000000..50d352e0cc3 --- /dev/null +++ b/cpp/downgrades/d77c09d8bdc172c9201dec293de1e14c931d3f05/upgrade.properties @@ -0,0 +1,2 @@ +description: Remove _Float128 type +compatibility: full diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index 162ce6d354b..e80332de277 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,18 @@ +## 0.9.0 + +### Breaking Changes + +* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed. +* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed. + +### Major Analysis Improvements + +* The `PrintAST` library now also prints global and namespace variables and their initializers. + +### Minor Analysis Improvements + +* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types. + ## 0.8.1 ### Deprecated APIs diff --git a/cpp/ql/lib/change-notes/released/0.9.0.md b/cpp/ql/lib/change-notes/released/0.9.0.md new file mode 100644 index 00000000000..eaace2d6e2e --- /dev/null +++ b/cpp/ql/lib/change-notes/released/0.9.0.md @@ -0,0 +1,14 @@ +## 0.9.0 + +### Breaking Changes + +* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed. +* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed. + +### Major Analysis Improvements + +* The `PrintAST` library now also prints global and namespace variables and their initializers. + +### Minor Analysis Improvements + +* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types. diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml index 2f693f95ba6..8b9fc185202 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.8.1 +lastReleaseVersion: 0.9.0 diff --git a/cpp/ql/lib/printAst.ql b/cpp/ql/lib/printAst.ql index ae6b3052ec7..2023beaec13 100644 --- a/cpp/ql/lib/printAst.ql +++ b/cpp/ql/lib/printAst.ql @@ -18,10 +18,10 @@ external string selectedSourceFile(); class Cfg extends PrintAstConfiguration { /** - * Holds if the AST for `func` should be printed. - * Print All functions from the selected file. + * Holds if the AST for `decl` should be printed. + * Print All declarations from the selected file. */ - override predicate shouldPrintFunction(Function func) { - func.getFile() = getFileBySourceArchiveName(selectedSourceFile()) + override predicate shouldPrintDeclaration(Declaration decl) { + decl.getFile() = getFileBySourceArchiveName(selectedSourceFile()) } } diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index e0aca5679e3..d35cb193197 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/cpp-all -version: 0.8.1 +version: 0.9.0 groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp library: true upgrades: upgrades dependencies: + codeql/dataflow: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/cpp/ql/lib/semmle/code/cpp/Print.qll b/cpp/ql/lib/semmle/code/cpp/Print.qll index 5df8f087689..28a18bb0ac4 100644 --- a/cpp/ql/lib/semmle/code/cpp/Print.qll +++ b/cpp/ql/lib/semmle/code/cpp/Print.qll @@ -6,11 +6,9 @@ private import PrintAST * that requests that function, or no `PrintASTConfiguration` exists. */ private predicate shouldPrintDeclaration(Declaration decl) { - not decl instanceof Function + not (decl instanceof Function or decl instanceof GlobalOrNamespaceVariable) or - not exists(PrintAstConfiguration c) - or - exists(PrintAstConfiguration config | config.shouldPrintFunction(decl)) + exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl)) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/PrintAST.ql b/cpp/ql/lib/semmle/code/cpp/PrintAST.ql index bf7e345132c..edf9ffec1e0 100644 --- a/cpp/ql/lib/semmle/code/cpp/PrintAST.ql +++ b/cpp/ql/lib/semmle/code/cpp/PrintAST.ql @@ -9,13 +9,13 @@ import cpp import PrintAST /** - * Temporarily tweak this class or make a copy to control which functions are + * Temporarily tweak this class or make a copy to control which declarations are * printed. */ class Cfg extends PrintAstConfiguration { /** * TWEAK THIS PREDICATE AS NEEDED. - * Holds if the AST for `func` should be printed. + * Holds if the AST for `decl` should be printed. */ - override predicate shouldPrintFunction(Function func) { any() } + override predicate shouldPrintDeclaration(Declaration decl) { any() } } diff --git a/cpp/ql/lib/semmle/code/cpp/PrintAST.qll b/cpp/ql/lib/semmle/code/cpp/PrintAST.qll index b552c6eeb38..46d9b34d890 100644 --- a/cpp/ql/lib/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/lib/semmle/code/cpp/PrintAST.qll @@ -1,9 +1,9 @@ /** * Provides queries to pretty-print a C++ AST as a graph. * - * By default, this will print the AST for all functions in the database. To change this behavior, - * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions - * you wish to view the AST for. + * By default, this will print the AST for all functions and global and namespace variables in + * the database. To change this behavior, extend `PrintASTConfiguration` and override + * `shouldPrintDeclaration` to hold for only the declarations you wish to view the AST for. */ import cpp @@ -12,7 +12,7 @@ private import semmle.code.cpp.Print private newtype TPrintAstConfiguration = MkPrintAstConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintAstConfiguration extends TPrintAstConfiguration { /** @@ -21,14 +21,16 @@ class PrintAstConfiguration extends TPrintAstConfiguration { string toString() { result = "PrintASTConfiguration" } /** - * Holds if the AST for `func` should be printed. By default, holds for all - * functions. + * Holds if the AST for `decl` should be printed. By default, holds for all + * functions and global and namespace variables. Currently, does not support any + * other declaration types. */ - predicate shouldPrintFunction(Function func) { any() } + predicate shouldPrintDeclaration(Declaration decl) { any() } } -private predicate shouldPrintFunction(Function func) { - exists(PrintAstConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintDeclaration(Declaration decl) { + exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl)) and + (decl instanceof Function or decl instanceof GlobalOrNamespaceVariable) } bindingset[s] @@ -69,7 +71,7 @@ private predicate locationSortKeys(Locatable ast, string file, int line, int col ) } -private Function getEnclosingFunction(Locatable ast) { +private Declaration getAnEnclosingDeclaration(Locatable ast) { result = ast.(Expr).getEnclosingFunction() or result = ast.(Stmt).getEnclosingFunction() @@ -78,6 +80,10 @@ private Function getEnclosingFunction(Locatable ast) { or result = ast.(Parameter).getFunction() or + result = ast.(Expr).getEnclosingDeclaration() + or + result = ast.(Initializer).getDeclaration() + or result = ast } @@ -86,21 +92,21 @@ private Function getEnclosingFunction(Locatable ast) { * nodes for things like parameter lists and constructor init lists. */ private newtype TPrintAstNode = - TAstNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or + TAstNode(Locatable ast) { shouldPrintDeclaration(getAnEnclosingDeclaration(ast)) } or TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) { // We create a unique node for each pair of (stmt, entry), to avoid having one node with // multiple parents due to extractor bug CPP-413. stmt.getADeclarationEntry() = entry and - shouldPrintFunction(stmt.getEnclosingFunction()) + shouldPrintDeclaration(stmt.getEnclosingFunction()) } or - TParametersNode(Function func) { shouldPrintFunction(func) } or + TParametersNode(Function func) { shouldPrintDeclaration(func) } or TConstructorInitializersNode(Constructor ctor) { ctor.hasEntryPoint() and - shouldPrintFunction(ctor) + shouldPrintDeclaration(ctor) } or TDestructorDestructionsNode(Destructor dtor) { dtor.hasEntryPoint() and - shouldPrintFunction(dtor) + shouldPrintDeclaration(dtor) } /** @@ -158,10 +164,10 @@ class PrintAstNode extends TPrintAstNode { /** * Holds if this node should be printed in the output. By default, all nodes - * within a function are printed, but the query can override - * `PrintASTConfiguration.shouldPrintFunction` to filter the output. + * within functions and global and namespace variables are printed, but the query + * can override `PrintASTConfiguration.shouldPrintDeclaration` to filter the output. */ - final predicate shouldPrint() { shouldPrintFunction(this.getEnclosingFunction()) } + final predicate shouldPrint() { shouldPrintDeclaration(this.getEnclosingDeclaration()) } /** * Gets the children of this node. @@ -229,10 +235,15 @@ class PrintAstNode extends TPrintAstNode { abstract string getChildAccessorPredicateInternal(int childIndex); /** - * Gets the `Function` that contains this node. + * Gets the `Declaration` that contains this node. */ - private Function getEnclosingFunction() { - result = this.getParent*().(FunctionNode).getFunction() + private Declaration getEnclosingDeclaration() { result = this.getParent*().getDeclaration() } + + /** + * Gets the `Declaration` this node represents. + */ + private Declaration getDeclaration() { + result = this.(AstNode).getAst() and shouldPrintDeclaration(result) } } @@ -571,16 +582,53 @@ class DestructorDestructionsNode extends PrintAstNode, TDestructorDestructionsNo final Destructor getDestructor() { result = dtor } } +abstract private class FunctionOrGlobalOrNamespaceVariableNode extends AstNode { + override string toString() { result = qlClass(ast) + getIdentityString(ast) } + + private int getOrder() { + this = + rank[result](FunctionOrGlobalOrNamespaceVariableNode node, Declaration decl, string file, + int line, int column | + node.getAst() = decl and + locationSortKeys(decl, file, line, column) + | + node order by file, line, column, getIdentityString(decl) + ) + } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "semmle.order" and result = this.getOrder().toString() + } +} + +/** + * A node representing a `GlobalOrNamespaceVariable`. + */ +class GlobalOrNamespaceVariableNode extends FunctionOrGlobalOrNamespaceVariableNode { + GlobalOrNamespaceVariable var; + + GlobalOrNamespaceVariableNode() { var = ast } + + override PrintAstNode getChildInternal(int childIndex) { + childIndex = 0 and + result.(AstNode).getAst() = var.getInitializer() + } + + override string getChildAccessorPredicateInternal(int childIndex) { + childIndex = 0 and result = "getInitializer()" + } +} + /** * A node representing a `Function`. */ -class FunctionNode extends AstNode { +class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode { Function func; FunctionNode() { func = ast } - override string toString() { result = qlClass(func) + getIdentityString(func) } - override PrintAstNode getChildInternal(int childIndex) { childIndex = 0 and result.(ParametersNode).getFunction() = func @@ -604,31 +652,10 @@ class FunctionNode extends AstNode { or childIndex = 3 and result = "" } - - private int getOrder() { - this = - rank[result](FunctionNode node, Function function, string file, int line, int column | - node.getAst() = function and - locationSortKeys(function, file, line, column) - | - node order by file, line, column, getIdentityString(function) - ) - } - - override string getProperty(string key) { - result = super.getProperty(key) - or - key = "semmle.order" and result = this.getOrder().toString() - } - - /** - * Gets the `Function` this node represents. - */ - final Function getFunction() { result = func } } private string getChildAccessorWithoutConversions(Locatable parent, Element child) { - shouldPrintFunction(getEnclosingFunction(parent)) and + shouldPrintDeclaration(getAnEnclosingDeclaration(parent)) and ( exists(Stmt s | s = parent | namedStmtChildPredicates(s, child, result) @@ -647,7 +674,7 @@ private string getChildAccessorWithoutConversions(Locatable parent, Element chil } private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) { - shouldPrintFunction(getEnclosingFunction(s)) and + shouldPrintDeclaration(getAnEnclosingDeclaration(s)) and ( exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")") or @@ -735,7 +762,7 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) } private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) { - shouldPrintFunction(expr.getEnclosingFunction()) and + shouldPrintDeclaration(expr.getEnclosingDeclaration()) and ( expr.(Access).getTarget() = ele and pred = "getTarget()" or diff --git a/cpp/ql/lib/semmle/code/cpp/Type.qll b/cpp/ql/lib/semmle/code/cpp/Type.qll index 91354ef2e08..43757ce0b2d 100644 --- a/cpp/ql/lib/semmle/code/cpp/Type.qll +++ b/cpp/ql/lib/semmle/code/cpp/Type.qll @@ -814,9 +814,6 @@ private predicate floatingPointTypeMapping( // _Float128 kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false or - // _Float128x - kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true - or // _Float16 kind = 52 and base = 2 and domain = TRealDomain() and realKind = 52 and extended = false or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll index 56e636eab23..43bf134ea79 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll @@ -26,6 +26,8 @@ import cpp * global (inter-procedural) data flow analyses. */ deprecated module DataFlow { - import semmle.code.cpp.dataflow.internal.DataFlow + private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.code.cpp.dataflow.internal.DataFlowImpl1 } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll index f5a0cb85c48..d8bfca9ac2c 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll @@ -5,8 +5,8 @@ private import DataFlowUtil /** * Gets a function that might be called by `call`. */ -Function viableCallable(Call call) { - result = call.getTarget() +Function viableCallable(DataFlowCall call) { + result = call.(Call).getTarget() or // If the target of the call does not have a body in the snapshot, it might // be because the target is just a header declaration, and the real target @@ -58,13 +58,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam * Holds if the set of viable implementations that can be called by `call` * might be improved by knowing the call context. */ -predicate mayBenefitFromCallContext(Call call, Function f) { none() } +predicate mayBenefitFromCallContext(DataFlowCall call, Function f) { none() } /** * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ -Function viableImplInCallContext(Call call, Call ctx) { none() } +Function viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } /** A parameter position represented by an integer. */ class ParameterPosition extends int { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..f5c51b43e37 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..868c3ef6a2b 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplSpecific.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplSpecific.qll index 9c1d97428eb..7fa662bd691 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplSpecific.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides C++-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,10 @@ module Private { module Public { import DataFlowUtil } + +module CppOldDataFlow implements InputSig { + import Private + import Public + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index b380748fb3c..a6f00f30b27 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -153,10 +153,11 @@ predicate jumpStep(Node n1, Node n2) { none() } * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. */ -predicate storeStep(Node node1, Content f, PostUpdateNode node2) { +predicate storeStep(Node node1, ContentSet f, Node node2) { exists(ClassAggregateLiteral aggr, Field field | - // The following line requires `node2` to be both an `ExprNode` and a + // The following lines requires `node2` to be both an `ExprNode` and a // `PostUpdateNode`, which means it must be an `ObjectInitializerNode`. + node2 instanceof PostUpdateNode and node2.asExpr() = aggr and f.(FieldContent).getField() = field and aggr.getAFieldExpr(field) = node1.asExpr() @@ -167,12 +168,13 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) { node1.asExpr() = a and a.getLValue() = fa ) and - node2.getPreUpdateNode().asExpr() = fa.getQualifier() and + node2.(PostUpdateNode).getPreUpdateNode().asExpr() = fa.getQualifier() and f.(FieldContent).getField() = fa.getTarget() ) or exists(ConstructorFieldInit cfi | - node2.getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = cfi and + node2.(PostUpdateNode).getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = + cfi and f.(FieldContent).getField() = cfi.getTarget() and node1.asExpr() = cfi.getExpr() ) @@ -183,7 +185,7 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) { * Thus, `node1` references an object with a field `f` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content f, Node node2) { +predicate readStep(Node node1, ContentSet f, Node node2) { exists(FieldAccess fr | node1.asExpr() = fr.getQualifier() and fr.getTarget() = f.(FieldContent).getField() and @@ -195,7 +197,7 @@ predicate readStep(Node node1, Content f, Node node2) { /** * Holds if values stored inside content `c` are cleared at node `n`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { none() // stub implementation } @@ -235,12 +237,6 @@ class CastNode extends Node { CastNode() { none() } // stub implementation } -/** - * Holds if `n` should never be skipped over in the `PathGraph` and in path - * explanations. - */ -predicate neverSkipInPathGraph(Node n) { none() } - class DataFlowCallable = Function; class DataFlowExpr = Expr; @@ -265,8 +261,6 @@ class DataFlowCall extends Expr instanceof Call { predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/new/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/new/DataFlow.qll index c14ea8b7e90..ea4218da734 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/new/DataFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/new/DataFlow.qll @@ -26,6 +26,8 @@ import cpp * global (inter-procedural) data flow analyses. */ module DataFlow { - import semmle.code.cpp.ir.dataflow.internal.DataFlow + private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1 } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll index 93aa923a500..b2c749ceb4b 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll @@ -152,7 +152,19 @@ class Expr extends StmtParent, @expr { else result = this.getValue() } - /** Holds if this expression has a value that can be determined at compile time. */ + /** + * Holds if this expression has a value that can be determined at compile time. + * + * An expression has a value that can be determined at compile time when: + * - it is a compile-time constant, e.g., a literal value or the result of a constexpr + * compile-time constant; + * - it is an address of a (member) function, an address of a constexpr variable + * initialized to a constant address, or an address of an lvalue, or any of the + * previous with a constant value added to or subtracted from the address; + * - it is a reference to a (member) function, a reference to a constexpr variable + * initialized to a constant address, or a reference to an lvalue; + * - it is a non-template parameter of a uninstantiated template. + */ cached predicate isConstant() { valuebind(_, underlyingElement(this)) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/PrintIR.qll index c4ebf2f1eba..19ba7159089 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ import implementation.aliased_ssa.PrintIR diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DataFlow.qll index 32c236fec2c..a2dd75d635c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DataFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DataFlow.qll @@ -22,6 +22,8 @@ import cpp module DataFlow { - import semmle.code.cpp.ir.dataflow.internal.DataFlow + private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1 } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index 707a07deee6..ce8fc7ebe51 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -9,7 +9,7 @@ private import DataFlowImplCommon as DataFlowImplCommon * Gets a function that might be called by `call`. */ cached -Function viableCallable(CallInstruction call) { +DataFlowCallable viableCallable(DataFlowCall call) { DataFlowImplCommon::forceCachingInSameStage() and result = call.getStaticCallTarget() or @@ -235,7 +235,7 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam * Holds if the set of viable implementations that can be called by `call` * might be improved by knowing the call context. */ -predicate mayBenefitFromCallContext(CallInstruction call, Function f) { +predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { mayBenefitFromCallContext(call, f, _) } @@ -259,7 +259,7 @@ private predicate mayBenefitFromCallContext( * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ -Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { +DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { result = viableCallable(call) and exists(int i, Function f | mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..f3e52187647 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index b0de9745816..1975ac9781f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..266693f45f6 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll index 4ea383b20a1..ed4db065532 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides IR-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,10 @@ module Private { module Public { import DataFlowUtil } + +module CppDataFlow implements InputSig { + import Private + import Public + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index a9260c5feb3..6b3f745a641 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -681,9 +681,7 @@ predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolea * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. */ -predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { - storeStepImpl(node1, c, node2, _) -} +predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) } /** * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like @@ -744,7 +742,7 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex) * Thus, `node1` references an object with a field `f` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content c, Node node2) { +predicate readStep(Node node1, ContentSet c, Node node2) { exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 | nodeHasOperand(node2, operand, indirectionIndex2) and // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct @@ -767,7 +765,7 @@ predicate readStep(Node node1, Content c, Node node2) { /** * Holds if values stored inside content `c` are cleared at node `n`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { n = any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun) .getPreUpdateNode() and @@ -792,7 +790,7 @@ predicate clearsContent(Node n, Content c) { storeStepImpl(_, d, pun, true) and pun.getPreUpdateNode() = n | - c.getIndirectionIndex() = d.getIndirectionIndex() + c.(Content).getIndirectionIndex() = d.getIndirectionIndex() ) ) } @@ -833,12 +831,6 @@ class CastNode extends Node { CastNode() { none() } // stub implementation } -/** - * Holds if `n` should never be skipped over in the `PathGraph` and in path - * explanations. - */ -predicate neverSkipInPathGraph(Node n) { none() } - /** * A function that may contain code or a variable that may contain itself. When * flow crosses from one _enclosing callable_ to another, the interprocedural @@ -853,7 +845,7 @@ class DataFlowType = Type; /** A function call relevant for data flow. */ class DataFlowCall extends CallInstruction { - Function getEnclosingCallable() { result = this.getEnclosingFunction() } + DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() } } module IsUnreachableInCall { @@ -924,8 +916,6 @@ module IsUnreachableInCall { import IsUnreachableInCall -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index b9106a7bfc7..c4b18d9cb61 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ private import internal.IRInternal @@ -16,7 +16,7 @@ import Imports::IRConfiguration private newtype TPrintIRConfiguration = MkPrintIRConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { /** Gets a textual representation of this configuration. */ @@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration { /** * Holds if the IR for `func` should be printed. By default, holds for all - * functions. + * functions, global and namespace variables, and static local variables. */ - predicate shouldPrintFunction(Language::Declaration decl) { any() } + predicate shouldPrintDeclaration(Language::Declaration decl) { any() } } /** @@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration { */ private class FilteredIRConfiguration extends IRConfiguration { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { - shouldPrintFunction(func) + shouldPrintDeclaration(func) } } -private predicate shouldPrintFunction(Language::Declaration decl) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) +private predicate shouldPrintDeclaration(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) } private predicate shouldPrintInstruction(Instruction i) { @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) { } private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableInstruction(Instruction instr) { - shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction()) + shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index b9106a7bfc7..c4b18d9cb61 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ private import internal.IRInternal @@ -16,7 +16,7 @@ import Imports::IRConfiguration private newtype TPrintIRConfiguration = MkPrintIRConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { /** Gets a textual representation of this configuration. */ @@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration { /** * Holds if the IR for `func` should be printed. By default, holds for all - * functions. + * functions, global and namespace variables, and static local variables. */ - predicate shouldPrintFunction(Language::Declaration decl) { any() } + predicate shouldPrintDeclaration(Language::Declaration decl) { any() } } /** @@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration { */ private class FilteredIRConfiguration extends IRConfiguration { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { - shouldPrintFunction(func) + shouldPrintDeclaration(func) } } -private predicate shouldPrintFunction(Language::Declaration decl) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) +private predicate shouldPrintDeclaration(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) } private predicate shouldPrintInstruction(Instruction i) { @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) { } private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableInstruction(Instruction instr) { - shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction()) + shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index b9106a7bfc7..c4b18d9cb61 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ private import internal.IRInternal @@ -16,7 +16,7 @@ import Imports::IRConfiguration private newtype TPrintIRConfiguration = MkPrintIRConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { /** Gets a textual representation of this configuration. */ @@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration { /** * Holds if the IR for `func` should be printed. By default, holds for all - * functions. + * functions, global and namespace variables, and static local variables. */ - predicate shouldPrintFunction(Language::Declaration decl) { any() } + predicate shouldPrintDeclaration(Language::Declaration decl) { any() } } /** @@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration { */ private class FilteredIRConfiguration extends IRConfiguration { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { - shouldPrintFunction(func) + shouldPrintDeclaration(func) } } -private predicate shouldPrintFunction(Language::Declaration decl) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) +private predicate shouldPrintDeclaration(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) } private predicate shouldPrintInstruction(Instruction i) { @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) { } private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableInstruction(Instruction instr) { - shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction()) + shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll b/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll index 61821ee5bca..43120e1c7f0 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll @@ -1,6 +1,54 @@ /** * This file provides the first phase of the `cpp/invalid-pointer-deref` query that identifies flow * from an allocation to a pointer-arithmetic instruction that constructs a pointer that is out of bounds. + * + * Consider the following snippet: + * ```cpp + * 1. char* base = (char*)malloc(size); + * 2. char* end = base + size; + * 3. for(int *p = base; p <= end; p++) { + * 4. use(*p); // BUG: Should have been bounded by `p < end`. + * 5. } + * ``` + * this file identifies the flow from `new int[size]` to `base + size`. + * + * This is done using the product-flow library. The configuration tracks flow from the pair + * `(allocation, size of allocation)` to a pair `(a, b)` where there exists a pointer-arithmetic instruction + * `pai = a + r` such that `b` is a dataflow node where `b <= r`. Because there will be a dataflow-path from + * `allocation` to `a` this means that the `pai` will compute a pointer that is some number of elements beyond + * the end position of the allocation. See `pointerAddInstructionHasBounds` for the implementation of this. + * + * In the above example, the pair `(a, b)` is `(base, size)` with `base` and `size` coming from the expression + * `base + size` on line 2, which is also the pointer-arithmetic instruction. In general, the pair does not necessarily + * correspond directly to the operands of the pointer-arithmetic instruction. + * In the following example, the pair is again `(base, size)`, but with `base` coming from line 3 and `size` from line 2, + * and the pointer-arithmetic instruction being `base + n` on line 3: + * ```cpp + * 1. int* base = new int[size]; + * 2. if(n <= size) { + * 3. int* end = base + n; + * 4. for(int* p = base; p <= end; ++p) { + * 5. *p = 0; // BUG: Should have been bounded by `p < end`. + * 6. } + * 7. } + * ``` + * + * Handling false positives: + * + * Consider a snippet such as: + * ```cpp + * 1. int* base = new int[size]; + * 2. int n = condition() ? size : 0; + * 3. if(n >= size) return; + * 4. int* end = base + n; + * 5. for(int* p = base; p <= end; ++p) { + * 6. *p = 0; // This is fine since `end < base + size` + * 7. } + * ``` + * In order to remove this false positive we define a barrier (see `SizeBarrier::SizeBarrierConfig`) that finds the + * possible guards that compares a value to the size of the allocation. In the above example, this is the `(n >= size)` + * guard on line 3. `SizeBarrier::getABarrierNode` then defines any node that is guarded by such a guard as a barrier + * in the dataflow configuration. */ private import cpp @@ -42,16 +90,14 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) { * ``` * In this case, the sink pair identified by the product flow library (without any additional barriers) * would be `(p, n)` (where `n` is the `n` in `p[n]`), because there exists a pointer-arithmetic - * instruction `pai` such that: - * 1. The left-hand of `pai` flows from the allocation, and - * 2. The right-hand of `pai` is non-strictly upper bounded by `n` (where `n` is the `n` in `p[n]`) + * instruction `pai = a + b` such that: + * 1. the allocation flows to `a`, and + * 2. `b <= n` where `n` is the `n` in `p[n]` * but because there's a strict comparison that compares `n` against the size of the allocation this * snippet is fine. */ -module Barrier2 { - private class FlowState2 = int; - - private module BarrierConfig2 implements DataFlow::ConfigSig { +module SizeBarrier { + private module SizeBarrierConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { // The sources is the same as in the sources for the second // projection in the `AllocToInvalidPointerConfig` module. @@ -59,19 +105,19 @@ module Barrier2 { } additional predicate isSink( - DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, FlowState2 state, - boolean testIsTrue + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue ) { - // The sink is any "large" side of a relational comparison. - g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + // The sink is any "large" side of a relational comparison. i.e., the `right` expression + // in a guard such as `left < right + k`. + g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue) } predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } } - private import DataFlow::Global + private import DataFlow::Global - private FlowState2 getAFlowStateForNode(DataFlow::Node node) { + private int getAFlowStateForNode(DataFlow::Node node) { exists(DataFlow::Node source | flow(source, node) and hasSize(_, source, result) @@ -79,14 +125,14 @@ module Barrier2 { } private predicate operandGuardChecks( - IRGuardCondition g, Operand left, Operand right, FlowState2 state, boolean edge + IRGuardCondition g, Operand left, Operand right, int state, boolean edge ) { - exists(DataFlow::Node nLeft, DataFlow::Node nRight, FlowState2 state0 | + exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k | nRight.asOperand() = right and nLeft.asOperand() = left and - BarrierConfig2::isSink(nLeft, nRight, g, state0, edge) and + SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and state = getAFlowStateForNode(nRight) and - state0 <= state + k <= state ) } @@ -94,7 +140,7 @@ module Barrier2 { * Gets an instruction that is guarded by a guard condition which ensures that * the value of the instruction is upper-bounded by size of some allocation. */ - Instruction getABarrierInstruction(FlowState2 state) { + Instruction getABarrierInstruction(int state) { exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge | use = value.getAUse() and operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, @@ -108,7 +154,7 @@ module Barrier2 { * Gets a `DataFlow::Node` that is guarded by a guard condition which ensures that * the value of the node is upper-bounded by size of some allocation. */ - DataFlow::Node getABarrierNode(FlowState2 state) { + DataFlow::Node getABarrierNode(int state) { result.asOperand() = getABarrierInstruction(state).getAUse() } @@ -116,9 +162,7 @@ module Barrier2 { * Gets the block of a node that is guarded (see `getABarrierInstruction` or * `getABarrierNode` for the definition of what it means to be guarded). */ - IRBlock getABarrierBlock(FlowState2 state) { - result.getAnInstruction() = getABarrierInstruction(state) - } + IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) } } private module InterestingPointerAddInstruction { @@ -151,24 +195,8 @@ private module InterestingPointerAddInstruction { } /** - * A product-flow configuration for flow from an (allocation, size) pair to a - * pointer-arithmetic operation that is non-strictly upper-bounded by `allocation + size`. - * - * The goal of this query is to find patterns such as: - * ```cpp - * 1. char* begin = (char*)malloc(size); - * 2. char* end = begin + size; - * 3. for(int *p = begin; p <= end; p++) { - * 4. use(*p); - * 5. } - * ``` - * - * We do this by splitting the task up into two configurations: - * 1. `AllocToInvalidPointerConfig` find flow from `malloc(size)` to `begin + size`, and - * 2. `InvalidPointerToDerefConfig` finds flow from `begin + size` to an `end` (on line 3). - * - * Finally, the range-analysis library will find a load from (or store to) an address that - * is non-strictly upper-bounded by `end` (which in this case is `*p`). + * A product-flow configuration for flow from an `(allocation, size)` pair to a + * pointer-arithmetic operation `pai` such that `pai <= allocation + size`. */ private module Config implements ProductFlow::StateConfigSig { class FlowState1 = Unit; @@ -176,7 +204,7 @@ private module Config implements ProductFlow::StateConfigSig { class FlowState2 = int; predicate isSourcePair( - DataFlow::Node source1, FlowState1 state1, DataFlow::Node source2, FlowState2 state2 + DataFlow::Node allocSource, FlowState1 unit, DataFlow::Node sizeSource, FlowState2 sizeAddend ) { // In the case of an allocation like // ```cpp @@ -184,21 +212,21 @@ private module Config implements ProductFlow::StateConfigSig { // ``` // we use `state2` to remember that there was an offset (in this case an offset of `1`) added // to the size of the allocation. This state is then checked in `isSinkPair`. - exists(state1) and - hasSize(source1.asConvertedExpr(), source2, state2) + exists(unit) and + hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend) } predicate isSinkPair( - DataFlow::Node sink1, FlowState1 state1, DataFlow::Node sink2, FlowState2 state2 + DataFlow::Node allocSink, FlowState1 unit, DataFlow::Node sizeSink, FlowState2 sizeAddend ) { - exists(state1) and + exists(unit) and // We check that the delta computed by the range analysis matches the // state value that we set in `isSourcePair`. - pointerAddInstructionHasBounds0(_, sink1, sink2, state2) + pointerAddInstructionHasBounds0(_, allocSink, sizeSink, sizeAddend) } predicate isBarrier2(DataFlow::Node node, FlowState2 state) { - node = Barrier2::getABarrierNode(state) + node = SizeBarrier::getABarrierNode(state) } predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) } @@ -211,7 +239,7 @@ private module Config implements ProductFlow::StateConfigSig { private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState; /** - * Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the + * Holds if `pai` is non-strictly upper bounded by `sizeSink + delta` and `allocSink` is the * left operand of the pointer-arithmetic operation. * * For example in, @@ -220,37 +248,37 @@ private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState; * ``` * We will have: * - `pai` is `p + (size + 1)`, - * - `sink1` is `p` - * - `sink2` is `size` + * - `allocSink` is `p` + * - `sizeSink` is `size` * - `delta` is `1`. */ pragma[nomagic] private predicate pointerAddInstructionHasBounds0( - PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta + PointerAddInstruction pai, DataFlow::Node allocSink, DataFlow::Node sizeSink, int delta ) { InterestingPointerAddInstruction::isInteresting(pragma[only_bind_into](pai)) and - exists(Instruction right, Instruction instr2 | + exists(Instruction right, Instruction sizeInstr | pai.getRight() = right and - pai.getLeft() = sink1.asInstruction() and - instr2 = sink2.asInstruction() and - // pai.getRight() <= sink2 + delta - bounded1(right, instr2, delta) and - not right = Barrier2::getABarrierInstruction(delta) and - not instr2 = Barrier2::getABarrierInstruction(delta) + pai.getLeft() = allocSink.asInstruction() and + sizeInstr = sizeSink.asInstruction() and + // pai.getRight() <= sizeSink + delta + bounded1(right, sizeInstr, delta) and + not right = SizeBarrier::getABarrierInstruction(delta) and + not sizeInstr = SizeBarrier::getABarrierInstruction(delta) ) } /** - * Holds if `allocation` flows to `sink1` and `sink1` represents the left-hand - * side of the pointer-arithmetic instruction `pai`, and the right-hand side of `pai` - * is non-strictly upper bounded by the size of `alllocation` + `delta`. + * Holds if `allocation` flows to `allocSink` and `allocSink` represents the left operand + * of the pointer-arithmetic instruction `pai = a + b` (i.e., `allocSink = a`), and + * `b <= allocation + delta`. */ pragma[nomagic] predicate pointerAddInstructionHasBounds( - DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node sink1, int delta + DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node allocSink, int delta ) { - exists(DataFlow::Node sink2 | - AllocToInvalidPointerFlow::flow(allocation, _, sink1, sink2) and - pointerAddInstructionHasBounds0(pai, sink1, sink2, delta) + exists(DataFlow::Node sizeSink | + AllocToInvalidPointerFlow::flow(allocation, _, allocSink, sizeSink) and + pointerAddInstructionHasBounds0(pai, allocSink, sizeSink, delta) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll b/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll index 3eb0a2da670..d2c67289e4c 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll @@ -2,6 +2,75 @@ * This file provides the second phase of the `cpp/invalid-pointer-deref` query that identifies flow * from the out-of-bounds pointer identified by the `AllocationToInvalidPointer.qll` library to * a dereference of the out-of-bounds pointer. + * + * Consider the following snippet: + * ```cpp + * 1. char* base = (char*)malloc(size); + * 2. char* end = base + size; + * 3. for(char *p = base; p <= end; p++) { + * 4. use(*p); // BUG: Should have been bounded by `p < end`. + * 5. } + * ``` + * this file identifies the flow from `base + size` to `end`. We call `base + size` the "dereference source" and `end` + * the "dereference sink" (even though `end` is not actually dereferenced we will use this term because we will perform + * dataflow to find a use of a pointer `x` such that `x <= end` which is dereferenced. In the above example, `x` is `p` + * on line 4). + * + * Merely _constructing_ a pointer that's out-of-bounds is fine if the pointer is never dereferenced (in reality, the + * standard only guarantees that it is safe to move the pointer one element past the last element, but we ignore that + * here). So this step is about identifying which of the out-of-bounds pointers found by `pointerAddInstructionHasBounds` + * in `AllocationToInvalidPointer.qll` are actually being dereferenced. We do this using a regular dataflow + * configuration (see `InvalidPointerToDerefConfig`). + * + * The dataflow traversal defines the set of sources as any dataflow node `n` such that there exists a pointer-arithmetic + * instruction `pai` found by `AllocationToInvalidPointer.qll` and a `n.asInstruction() >= pai + deltaDerefSourceAndPai`. + * Here, `deltaDerefSourceAndPai` is the constant difference between the source we track for finding a dereference and the + * pointer-arithmetic instruction. + * + * The set of sinks is defined as any dataflow node `n` such that `addr <= n.asInstruction() + deltaDerefSinkAndDerefAddress` + * for some address operand `addr` and constant difference `deltaDerefSinkAndDerefAddress`. Since an address operand is + * always consumed by an instruction that performs a dereference this lets us identify a "bad dereference". We call the + * instruction that consumes the address operand the "operation". + * + * For example, consider the flow from `base + size` to `end` above. The sink is `end` on line 3 because + * `p <= end.asInstruction() + deltaDerefSinkAndDerefAddress`, where `p` is the address operand in `use(*p)` and + * `deltaDerefSinkAndDerefAddress >= 0`. The load attached to `*p` is the "operation". To ensure that the path makes + * intuitive sense, we only pick operations that are control-flow reachable from the dereference sink. + * + * To compute how many elements the dereference is beyond the end position of the allocation, we sum the two deltas + * `deltaDerefSourceAndPai` and `deltaDerefSinkAndDerefAddress`. This is done in the `operationIsOffBy` predicate + * (which is the only predicate exposed by this file). + * + * Handling false positives: + * + * Consider the following snippet: + * ```cpp + * 1. char *p = new char[size]; + * 2. char *end = p + size; + * 3. if (p < end) { + * 4. p += 1; + * 5. } + * 6. if (p < end) { + * 7. int val = *p; // GOOD + * 8. } + * ``` + * this is safe because `p` is guarded to be strictly less than `end` on line 6 before the dereference on line 7. However, if we + * run the query on the above without further modifications we would see an alert on line 7. This is because range analysis infers + * that `p <= end` after the increment on line 4, and thus the result of `p += 1` is seen as a valid dereference source. This + * node then flows to `p` on line 6 (which is a valid dereference sink since it non-strictly upper bounds an address operand), and + * range analysis then infers that the address operand of `*p` (i.e., `p`) is non-strictly upper bounded by `p`, and thus reports + * an alert on line 7. + * + * In order to handle the above false positive, we define a barrier that identifies guards such as `p < end` that ensures that a value + * is less than the pointer-arithmetic instruction that computed the invalid pointer. This is done in the `InvalidPointerToDerefBarrier` + * module. Since the node we are tracking is not necessarily _equal_ to the pointer-arithmetic instruction, but rather satisfies + * `node.asInstruction() <= pai + deltaDerefSourceAndPai`, we need to account for the delta when checking if a guard is sufficiently + * strong to infer that a future dereference is safe. To do this, we check that the guard guarantees that a node `n` satisfies + * `n < node + k` where `node` is a node we know is equal to the value of the dereference source (i.e., it satisfies + * `node.asInstruction() <= pai + deltaDerefSourceAndPai`) and `k <= deltaDerefSourceAndPai`. Combining this we have + * `n < node + k <= node + deltaDerefSourceAndPai <= pai + 2*deltaDerefSourceAndPai` (TODO: Oops. This math doesn't quite work out. + * I think this is because we need to redefine the `BarrierConfig` to start flow at the pointer-arithmetic instruction instead of + * at the dereference source. When combined with TODO above it's easy to show that this guard ensures that the dereference is safe). */ private import cpp @@ -19,10 +88,10 @@ private module InvalidPointerToDerefBarrier { } additional predicate isSink( - DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue ) { // The sink is any "large" side of a relational comparison. - g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue) } predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } @@ -40,12 +109,12 @@ private module InvalidPointerToDerefBarrier { private predicate operandGuardChecks( IRGuardCondition g, Operand left, Operand right, int state, boolean edge ) { - exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 | + exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k | nRight.asOperand() = right and nLeft.asOperand() = left and - BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and + BarrierConfig::isSink(nLeft, nRight, g, k, edge) and state = getInvalidPointerToDerefSourceDelta(nRight) and - state0 <= state + k <= state ) } @@ -85,47 +154,48 @@ private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig { private import DataFlow::Global /** - * Holds if `source1` is dataflow node that represents an allocation that flows to the - * left-hand side of the pointer-arithmetic `pai`, and `derefSource` is a dataflow node with - * a pointer-value that is non-strictly upper bounded by `pai + delta`. + * Holds if `allocSource` is dataflow node that represents an allocation that flows to the + * left-hand side of the pointer-arithmetic `pai`, and `derefSource <= pai + derefSourcePaiDelta`. * * For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such * as `(p + size) + 1` and `derefSource` is the node representing `(p + size) + 1`. In this - * case `delta` is 1. + * case `derefSourcePaiDelta` is 1. */ private predicate invalidPointerToDerefSource( - DataFlow::Node source1, PointerArithmeticInstruction pai, DataFlow::Node derefSource, int delta + DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource, + int deltaDerefSourceAndPai ) { - exists(int delta0 | - // Note that `delta` is not necessarily equal to `delta0`: - // `delta0` is the constant offset added to the size of the allocation, and - // delta is the constant difference between the pointer-arithmetic instruction + exists(int rhsSizeDelta | + // Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`: + // `rhsSizeDelta` is the constant offset added to the size of the allocation, and + // `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction // and the instruction computing the address for which we will search for a dereference. - AllocToInvalidPointer::pointerAddInstructionHasBounds(source1, pai, _, delta0) and - bounded2(derefSource.asInstruction(), pai, delta) and - delta >= 0 and - // TODO: This condition will go away once #13725 is merged, and then we can make `Barrier2` + AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and + bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and + deltaDerefSourceAndPai >= 0 and + // TODO: This condition will go away once #13725 is merged, and then we can make `SizeBarrier` // private to `AllocationToInvalidPointer.qll`. - not derefSource.getBasicBlock() = AllocToInvalidPointer::Barrier2::getABarrierBlock(delta0) + not derefSource.getBasicBlock() = + AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta) ) } /** * Holds if `sink` is a sink for `InvalidPointerToDerefConfig` and `i` is a `StoreInstruction` that - * writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that - * reads from an address that non-strictly upper-bounds `sink`. + * writes to an address `addr` such that `addr <= sink`, or `i` is a `LoadInstruction` that + * reads from an address `addr` such that `addr <= sink`. */ pragma[inline] private predicate isInvalidPointerDerefSink( - DataFlow::Node sink, Instruction i, string operation, int delta + DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress ) { exists(AddressOperand addr, Instruction s, IRBlock b | s = sink.asInstruction() and - bounded(addr.getDef(), s, delta) and - delta >= 0 and + bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and + deltaDerefSinkAndDerefAddress >= 0 and i.getAnOperand() = addr and b = i.getBlock() and - not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta) + not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress) | i instanceof StoreInstruction and operation = "write" @@ -165,13 +235,13 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl */ private predicate derefSinkToOperation( DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation, - string description, int delta + string description, int deltaDerefSinkAndDerefAddress ) { - exists(Instruction i | + exists(Instruction operationInstr | paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and - isInvalidPointerDerefSink(derefSink, i, description, delta) and - i = getASuccessor(derefSink.asInstruction()) and - operation.asInstruction() = i + isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and + operationInstr = getASuccessor(derefSink.asInstruction()) and + operation.asInstruction() = operationInstr ) } diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index 19887dbd333..d77c09d8bdc 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -608,7 +608,7 @@ case @builtintype.kind of | 47 = @std_float64 // _Float64 | 48 = @float64x // _Float64x | 49 = @std_float128 // _Float128 -| 50 = @float128x // _Float128x +// ... 50 _Float128x | 51 = @char8_t | 52 = @float16 // _Float16 | 53 = @complex_float16 // _Complex _Float16 diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats index 24253b0aeb5..23f1331ee6d 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats @@ -16,14 +16,14 @@ @svnentry 575525 - - @location_stmt - 3813678 - @location_default 29769315 + + @location_stmt + 3813678 + @location_expr 13166528 @@ -90,7 +90,7 @@ @localvariable - 581207 + 581199 @enumconstant @@ -276,10 +276,6 @@ @std_float128 466 - - @float128x - 466 - @char8_t 466 @@ -296,10 +292,6 @@ @pointer 567483 - - @reference - 1747669 - @type_with_specifiers 1009066 @@ -313,8 +305,8 @@ 635462 - @rvalue_reference - 613493 + @reference + 1747669 @gnu_vector @@ -324,6 +316,10 @@ @routinereference 242 + + @rvalue_reference + 613493 + @block 10 @@ -364,10 +360,6 @@ @stdattribute 492036 - - @alignas - 9792 - @declspec 243716 @@ -377,12 +369,12 @@ 3 - @attribute_arg_token - 39168 + @alignas + 9792 - @attribute_arg_type - 466 + @attribute_arg_token + 39168 @attribute_arg_constant_expr @@ -396,6 +388,10 @@ @attribute_arg_constant 1 + + @attribute_arg_type + 466 + @derivation 368257 @@ -432,34 +428,18 @@ @lambdacapture 27977 - - @errorexpr - 46892 - @address_of 438815 - - @reference_to - 1592467 - @indirect 292170 - - @ref_indirect - 1938672 - @array_to_pointer 1428626 - - @vacuous_destructor_call - 8150 - @parexpr 3581934 @@ -468,6 +448,10 @@ @arithnegexpr 650875 + + @unaryplusexpr + 2911 + @complementexpr 27792 @@ -610,7 +594,7 @@ @assignorexpr - 23831 + 23830 @assignxorexpr @@ -620,6 +604,10 @@ @assignpaddexpr 13606 + + @assignpsubexpr + 1151 + @andlogicalexpr 249546 @@ -634,7 +622,7 @@ @subscriptexpr - 367604 + 367599 @callexpr @@ -644,6 +632,10 @@ @vastartexpr 3706 + + @vaargexpr + 950 + @vaendexpr 2797 @@ -656,34 +648,6 @@ @varaccess 6019333 - - @thisaccess - 1127035 - - - @new_expr - 47668 - - - @delete_expr - 11749 - - - @throw_expr - 21695 - - - @condition_decl - 42427 - - - @braced_init_list - 1108 - - - @type_id - 36483 - @runtime_sizeof 295357 @@ -693,8 +657,8 @@ 49891 - @sizeof_pack - 5595 + @expr_stmt + 94234 @routineexpr @@ -705,16 +669,12 @@ 1126930 - @isemptyexpr - 1481 + @offsetofexpr + 19960 - @ispodexpr - 634 - - - @hastrivialdestructor - 466 + @typescompexpr + 562865 @literal @@ -724,105 +684,33 @@ @aggregateliteral 913874 - - @delete_array_expr - 1406 - - - @new_array_expr - 5103 - - - @ctordirectinit - 112978 - - - @ctorvirtualinit - 6513 - - - @ctorfieldinit - 201118 - - - @ctordelegatinginit - 3351 - - - @dtordirectdestruct - 41776 - - - @dtorvirtualdestruct - 4128 - - - @dtorfielddestruct - 41705 - - - @static_cast - 210934 - - - @reinterpret_cast - 30752 - - - @const_cast - 35250 - - - @dynamic_cast - 1037 - @c_style_cast 4209495 - - @lambdaexpr - 21449 - - - @param_ref - 245656 - - - @istrivialexpr - 932 - - - @istriviallycopyableexpr - 3730 - - - @isconstructibleexpr - 466 - - - @isfinalexpr - 1693 - - - @noexceptexpr - 25737 - - - @builtinaddressof - 13302 - @temp_init 826674 - @assume - 3203 + @errorexpr + 46892 - @unaryplusexpr - 2911 + @reference_to + 1592467 + + + @ref_indirect + 1938672 + + + @vacuous_destructor_call + 8150 + + + @assume + 3203 @conjugation @@ -868,25 +756,41 @@ @maxexpr 1 - - @assignpsubexpr - 1151 - @virtfunptrexpr 1 - @vaargexpr - 950 + @thisaccess + 1127035 - @expr_stmt - 94234 + @new_expr + 47668 - @offsetofexpr - 19960 + @delete_expr + 11749 + + + @throw_expr + 21695 + + + @condition_decl + 42427 + + + @braced_init_list + 1108 + + + @type_id + 36483 + + + @sizeof_pack + 5595 @hasassignexpr @@ -944,10 +848,18 @@ @isconvtoexpr 104 + + @isemptyexpr + 1481 + @isenumexpr 522 + + @ispodexpr + 634 + @ispolyexpr 3 @@ -956,22 +868,82 @@ @isunionexpr 5 - - @typescompexpr - 562865 - @intaddrexpr 1 + + @hastrivialdestructor + 466 + @uuidof 20120 + + @delete_array_expr + 1406 + + + @new_array_expr + 5103 + @foldexpr 4 + + @ctordirectinit + 112978 + + + @ctorvirtualinit + 6513 + + + @ctorfieldinit + 201118 + + + @ctordelegatinginit + 3351 + + + @dtordirectdestruct + 41776 + + + @dtorvirtualdestruct + 4128 + + + @dtorfielddestruct + 41705 + + + @static_cast + 210934 + + + @reinterpret_cast + 30752 + + + @const_cast + 35250 + + + @dynamic_cast + 1037 + + + @lambdaexpr + 21449 + + + @param_ref + 245656 + @noopexpr 37 @@ -1000,10 +972,18 @@ @isnothrowassignableexpr 4183 + + @istrivialexpr + 932 + @isstandardlayoutexpr 2 + + @istriviallycopyableexpr + 3730 + @isliteraltypeexpr 2 @@ -1020,6 +1000,10 @@ @hasnothrowmoveassignexpr 4 + + @isconstructibleexpr + 466 + @isnothrowconstructibleexpr 14434 @@ -1056,6 +1040,14 @@ @isvalueclassexpr 1 + + @isfinalexpr + 1693 + + + @noexceptexpr + 25737 + @builtinshufflevector 1 @@ -1064,6 +1056,10 @@ @builtinchooseexpr 9050 + + @builtinaddressof + 13302 + @vec_fill 1 @@ -1232,6 +1228,10 @@ @stmt_while 30110 + + @stmt_goto + 110511 + @stmt_label 53056 @@ -1261,8 +1261,8 @@ 20753 - @stmt_try_block - 46921 + @stmt_asm + 109804 @stmt_decl @@ -1270,7 +1270,7 @@ @stmt_empty - 193324 + 193321 @stmt_continue @@ -1281,24 +1281,8 @@ 102331 - @stmt_range_based_for - 8393 - - - @stmt_handler - 65314 - - - @stmt_constexpr_if - 52508 - - - @stmt_goto - 110511 - - - @stmt_asm - 109804 + @stmt_try_block + 46921 @stmt_microsoft_try @@ -1316,6 +1300,18 @@ @stmt_assigned_goto 9061 + + @stmt_range_based_for + 8393 + + + @stmt_handler + 65314 + + + @stmt_constexpr_if + 52508 + @stmt_co_return 2 @@ -1356,10 +1352,6 @@ @ppd_undef 258328 - - @ppd_pragma - 312020 - @ppd_include_next 1865 @@ -1372,6 +1364,10 @@ @ppd_error 46 + + @ppd_pragma + 312020 + @ppd_objc_import 2 @@ -2008,7 +2004,7 @@ seconds - 10051 + 14798 @@ -2089,12 +2085,12 @@ 3 4 - 678 + 239 4 5 - 319 + 757 6 @@ -2109,31 +2105,31 @@ 10 11 - 79 - - - 11 - 12 - 159 - - - 13 - 17 119 - 17 + 11 + 14 + 159 + + + 14 + 17 + 79 + + + 18 21 159 21 - 44 + 45 159 - 55 - 89 + 64 + 130 79 @@ -2202,37 +2198,47 @@ 3 4 - 1236 + 398 4 5 - 518 + 1356 5 6 - 159 + 199 6 7 - 558 + 239 7 - 9 + 8 279 + + 8 + 9 + 39 + 9 - 24 + 10 + 319 + + + 11 + 28 279 - 24 - 91 - 279 + 28 + 96 + 199 @@ -2277,24 +2283,19 @@ 12 - - 3 - 4 - 39 - 4 5 + 79 + + + 182 + 183 39 - 120 - 121 - 39 - - - 129 - 130 + 189 + 190 39 @@ -2311,27 +2312,22 @@ 1 2 - 5823 + 10011 2 3 - 2074 + 3549 3 - 4 - 917 + 5 + 1116 - 4 - 6 - 917 - - - 6 - 40 - 319 + 18 + 47 + 119 @@ -2347,32 +2343,22 @@ 1 2 - 5065 + 9572 2 3 - 1794 + 3470 3 4 - 1236 + 1037 4 - 5 - 757 - - - 5 - 7 - 877 - - - 7 - 67 - 319 + 74 + 717 @@ -2388,12 +2374,12 @@ 1 2 - 9892 + 14518 2 - 3 - 159 + 4 + 279 @@ -2762,11 +2748,11 @@ cpu_seconds - 7838 + 7389 elapsed_seconds - 161 + 138 @@ -2812,17 +2798,17 @@ 1 2 - 6605 + 5948 2 3 - 853 + 979 3 - 12 - 380 + 15 + 461 @@ -2838,12 +2824,12 @@ 1 2 - 7354 + 6778 2 3 - 484 + 610 @@ -2859,56 +2845,51 @@ 1 2 - 23 + 11 2 3 - 23 - - - 4 - 5 - 11 + 34 7 8 - 23 - - - 11 - 12 11 - 25 - 26 + 9 + 10 11 - 52 - 53 + 12 + 13 11 - 114 - 115 + 37 + 38 11 - 145 - 146 + 150 + 151 11 - 244 - 245 + 172 + 173 11 - 248 - 249 + 211 + 212 + 11 + + + 258 + 259 11 @@ -2925,56 +2906,51 @@ 1 2 - 23 + 11 2 3 - 23 - - - 4 - 5 - 11 + 34 7 8 - 23 - - - 11 - 12 11 - 25 - 26 + 9 + 10 11 - 51 - 52 + 12 + 13 11 - 102 - 103 + 36 + 37 11 - 107 - 108 + 125 + 126 11 - 172 - 173 + 126 + 127 11 - 230 - 231 + 145 + 146 + 11 + + + 227 + 228 11 @@ -12311,7 +12287,7 @@ 2 3 - 543265 + 543264 3 @@ -12346,7 +12322,7 @@ 11 337 - 224500 + 224502 339 @@ -18397,11 +18373,11 @@ localvariables - 581207 + 581199 id - 581207 + 581199 type_id @@ -18409,7 +18385,7 @@ name - 91326 + 91325 @@ -18423,7 +18399,7 @@ 1 2 - 581207 + 581199 @@ -18439,7 +18415,7 @@ 1 2 - 581207 + 581199 @@ -18532,7 +18508,7 @@ 1 2 - 57522 + 57521 2 @@ -18568,7 +18544,7 @@ 1 2 - 77150 + 77148 2 @@ -19606,19 +19582,19 @@ builtintypes - 22848 + 22382 id - 22848 + 22382 name - 22848 + 22382 kind - 22848 + 22382 size @@ -19644,7 +19620,7 @@ 1 2 - 22848 + 22382 @@ -19660,7 +19636,7 @@ 1 2 - 22848 + 22382 @@ -19676,7 +19652,7 @@ 1 2 - 22848 + 22382 @@ -19692,7 +19668,7 @@ 1 2 - 22848 + 22382 @@ -19708,7 +19684,7 @@ 1 2 - 22848 + 22382 @@ -19724,7 +19700,7 @@ 1 2 - 22848 + 22382 @@ -19740,7 +19716,7 @@ 1 2 - 22848 + 22382 @@ -19756,7 +19732,7 @@ 1 2 - 22848 + 22382 @@ -19772,7 +19748,7 @@ 1 2 - 22848 + 22382 @@ -19788,7 +19764,7 @@ 1 2 - 22848 + 22382 @@ -19804,7 +19780,7 @@ 1 2 - 22848 + 22382 @@ -19820,7 +19796,7 @@ 1 2 - 22848 + 22382 @@ -19836,7 +19812,7 @@ 1 2 - 22848 + 22382 @@ -19852,7 +19828,7 @@ 1 2 - 22848 + 22382 @@ -19868,7 +19844,7 @@ 1 2 - 22848 + 22382 @@ -19887,8 +19863,8 @@ 466 - 3 - 4 + 2 + 3 466 @@ -19928,8 +19904,8 @@ 466 - 3 - 4 + 2 + 3 466 @@ -19969,8 +19945,8 @@ 466 - 3 - 4 + 2 + 3 466 @@ -20057,8 +20033,8 @@ 466 - 31 - 32 + 30 + 31 466 @@ -20083,8 +20059,8 @@ 466 - 31 - 32 + 30 + 31 466 @@ -20109,8 +20085,8 @@ 466 - 31 - 32 + 30 + 31 466 @@ -20177,8 +20153,8 @@ 466 - 12 - 13 + 11 + 12 466 @@ -20213,8 +20189,8 @@ 466 - 12 - 13 + 11 + 12 466 @@ -20249,8 +20225,8 @@ 466 - 12 - 13 + 11 + 12 466 @@ -21468,7 +21444,7 @@ name - 1348530 + 1348063 kind @@ -21528,7 +21504,7 @@ 3 7 - 104916 + 104450 7 @@ -21559,12 +21535,12 @@ 2 3 - 125900 + 124967 3 7 - 14921 + 15387 @@ -21689,8 +21665,8 @@ 466 - 740 - 741 + 739 + 740 466 @@ -26371,15 +26347,15 @@ unspecifiedtype - 10137757 + 10137290 type_id - 10137757 + 10137290 unspecified_type_id - 6813527 + 6813061 @@ -26393,7 +26369,7 @@ 1 2 - 10137757 + 10137290 @@ -26409,7 +26385,7 @@ 1 2 - 4583230 + 4582764 2 @@ -28996,11 +28972,11 @@ expr_isload - 4981760 + 4981370 expr_id - 4981760 + 4981370 @@ -31127,11 +31103,11 @@ kind - 3387 + 1163 location - 3592604 + 8474327 @@ -31176,68 +31152,73 @@ 1 - 3 - 246 + 10 + 88 - 4 - 14 - 282 + 12 + 18 + 88 - 14 - 38 - 282 + 26 + 100 + 88 - 42 - 83 - 282 + 105 + 305 + 88 - 85 - 142 - 282 + 320 + 417 + 88 - 145 - 339 - 282 + 466 + 784 + 88 - 364 - 564 - 282 + 892 + 1647 + 88 - 653 - 832 - 282 + 1656 + 2402 + 88 - 973 - 1185 - 282 + 3130 + 4067 + 88 - 1329 - 2628 - 282 + 4297 + 4915 + 88 - 3015 - 6253 - 282 + 5184 + 20397 + 88 - 6592 - 78904 - 282 + 25254 + 49060 + 88 - 109469 - 109470 - 35 + 63119 + 136808 + 88 + + + 285063 + 285064 + 17 @@ -31252,68 +31233,73 @@ 1 - 2 - 282 + 9 + 88 - 2 - 3 - 176 + 9 + 15 + 88 - 3 - 6 - 282 + 17 + 96 + 88 - 6 - 13 - 282 + 99 + 222 + 88 - 14 - 26 - 282 + 260 + 383 + 88 - 28 - 62 - 246 + 408 + 577 + 88 - 63 - 83 - 282 + 595 + 749 + 88 - 91 - 183 - 282 + 864 + 1774 + 88 - 206 - 342 - 282 + 1812 + 2545 + 88 - 353 - 448 - 282 + 2601 + 2910 + 88 - 469 - 1020 - 282 + 3419 + 4913 + 88 - 1051 - 14619 - 282 + 5471 + 21108 + 88 - 16978 - 32762 - 141 + 26251 + 76182 + 88 + + + 223904 + 223905 + 17 @@ -31329,32 +31315,22 @@ 1 2 - 1941777 + 7133647 2 3 - 838486 + 661870 3 - 4 - 247163 + 18 + 637071 - 4 - 8 - 284529 - - - 8 - 155 - 269533 - - - 155 - 53477 - 11114 + 18 + 71656 + 41738 @@ -31370,22 +31346,17 @@ 1 2 - 2392776 + 7239544 2 3 - 875922 + 617153 3 - 6 - 306969 - - - 6 - 25 - 16936 + 32 + 617629 @@ -35621,11 +35592,11 @@ stmt_decl_bind - 585138 + 585129 stmt - 545022 + 545014 num @@ -35633,7 +35604,7 @@ decl - 585033 + 585024 @@ -35647,12 +35618,12 @@ 1 2 - 524159 + 524152 2 19 - 20862 + 20861 @@ -35668,12 +35639,12 @@ 1 2 - 524159 + 524152 2 19 - 20862 + 20861 @@ -35871,7 +35842,7 @@ 1 2 - 584995 + 584986 2 @@ -35892,7 +35863,7 @@ 1 2 - 585033 + 585024 @@ -35902,11 +35873,11 @@ stmt_decl_entry_bind - 527594 + 527587 stmt - 487781 + 487773 num @@ -35914,7 +35885,7 @@ decl_entry - 527536 + 527528 @@ -35928,7 +35899,7 @@ 1 2 - 467183 + 467176 2 @@ -35949,7 +35920,7 @@ 1 2 - 467183 + 467176 2 @@ -36152,7 +36123,7 @@ 1 2 - 527515 + 527507 3 @@ -36173,7 +36144,7 @@ 1 2 - 527536 + 527528 diff --git a/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/builtintypes.ql b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/builtintypes.ql new file mode 100644 index 00000000000..5d1b39088b6 --- /dev/null +++ b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/builtintypes.ql @@ -0,0 +1,13 @@ +class BuiltinType extends @builtintype { + string toString() { none() } +} + +predicate isFloat128xBuiltinType(BuiltinType type) { + exists(int kind | builtintypes(type, _, kind, _, _, _) | kind = 50) +} + +from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment +where + builtintypes(type, name, kind, size, sign, alignment) and + if isFloat128xBuiltinType(type) then kind_new = 1 else kind_new = kind +select type, name, kind_new, size, sign, alignment diff --git a/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/old.dbscheme b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/old.dbscheme new file mode 100644 index 00000000000..19887dbd333 --- /dev/null +++ b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/old.dbscheme @@ -0,0 +1,2212 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +case @macroinvocation.kind of + 1 = @macro_expansion +| 2 = @other_macro_reference +; + +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* +case @function.kind of + 1 = @normal_function +| 2 = @constructor +| 3 = @destructor +| 4 = @conversion_function +| 5 = @operator +| 6 = @builtin_function // GCC built-in functions, e.g. __builtin___memcpy_chk +; +*/ + +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point( + int id: @function ref, + unique int entry_point: @stmt ref +); + +function_return_type( + int id: @function ref, + int return_type: @type ref +); + +/** + * If `function` is a coroutine, then this gives the `std::experimental::resumable_traits` + * instance associated with it, and the variables representing the `handle` and `promise` + * for it. + */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type( + unique int id: @function ref, + int this_type: @type ref +); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides( + int new: @function ref, + int old: @function ref +); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +orphaned_variables( + int var: @localvariable ref, + int function: @function ref +) + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/** + * Built-in types are the fundamental types, e.g., integral, floating, and void. + */ +case @builtintype.kind of + 1 = @errortype +| 2 = @unknowntype +| 3 = @void +| 4 = @boolean +| 5 = @char +| 6 = @unsigned_char +| 7 = @signed_char +| 8 = @short +| 9 = @unsigned_short +| 10 = @signed_short +| 11 = @int +| 12 = @unsigned_int +| 13 = @signed_int +| 14 = @long +| 15 = @unsigned_long +| 16 = @signed_long +| 17 = @long_long +| 18 = @unsigned_long_long +| 19 = @signed_long_long +// ... 20 Microsoft-specific __int8 +// ... 21 Microsoft-specific __int16 +// ... 22 Microsoft-specific __int32 +// ... 23 Microsoft-specific __int64 +| 24 = @float +| 25 = @double +| 26 = @long_double +| 27 = @complex_float // C99-specific _Complex float +| 28 = @complex_double // C99-specific _Complex double +| 29 = @complex_long_double // C99-specific _Complex long double +| 30 = @imaginary_float // C99-specific _Imaginary float +| 31 = @imaginary_double // C99-specific _Imaginary double +| 32 = @imaginary_long_double // C99-specific _Imaginary long double +| 33 = @wchar_t // Microsoft-specific +| 34 = @decltype_nullptr // C++11 +| 35 = @int128 // __int128 +| 36 = @unsigned_int128 // unsigned __int128 +| 37 = @signed_int128 // signed __int128 +| 38 = @float128 // __float128 +| 39 = @complex_float128 // _Complex __float128 +| 40 = @decimal32 // _Decimal32 +| 41 = @decimal64 // _Decimal64 +| 42 = @decimal128 // _Decimal128 +| 43 = @char16_t +| 44 = @char32_t +| 45 = @std_float32 // _Float32 +| 46 = @float32x // _Float32x +| 47 = @std_float64 // _Float64 +| 48 = @float64x // _Float64x +| 49 = @std_float128 // _Float128 +| 50 = @float128x // _Float128x +| 51 = @char8_t +| 52 = @float16 // _Float16 +| 53 = @complex_float16 // _Complex _Float16 +; + +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/** + * Derived types are types that are directly derived from existing types and + * point to, refer to, transform type data to return a new type. + */ +case @derivedtype.kind of + 1 = @pointer +| 2 = @reference +| 3 = @type_with_specifiers +| 4 = @array +| 5 = @gnu_vector +| 6 = @routineptr +| 7 = @routinereference +| 8 = @rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated +| 10 = @block +; + +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* +case @usertype.kind of + 1 = @struct +| 2 = @class +| 3 = @union +| 4 = @enum +| 5 = @typedef // classic C: typedef typedef type name +| 6 = @template +| 7 = @template_parameter +| 8 = @template_template_parameter +| 9 = @proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated +| 13 = @scoped_enum +| 14 = @using_alias // a using name = type style typedef +; +*/ + +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the frontend. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +| 4 = @attribute_arg_constant_expr +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_constant( + unique int arg: @attribute_arg ref, + int constant: @expr ref +) +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall( + unique int caller: @funbindexpr ref, + int kind: int ref +); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // frontend internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +| 335 = @blockassignexpr +| 336 = @issame +| 337 = @isfunction +| 338 = @islayoutcompatible +| 339 = @ispointerinterconvertiblebaseof +| 340 = @isarray +| 341 = @arrayrank +| 342 = @arrayextent +| 343 = @isarithmetic +| 344 = @iscompletetype +| 345 = @iscompound +| 346 = @isconst +| 347 = @isfloatingpoint +| 348 = @isfundamental +| 349 = @isintegral +| 350 = @islvaluereference +| 351 = @ismemberfunctionpointer +| 352 = @ismemberobjectpointer +| 353 = @ismemberpointer +| 354 = @isobject +| 355 = @ispointer +| 356 = @isreference +| 357 = @isrvaluereference +| 358 = @isscalar +| 359 = @issigned +| 360 = @isunsigned +| 361 = @isvoid +| 362 = @isvolatile +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + | @issame + | @isfunction + | @islayoutcompatible + | @ispointerinterconvertiblebaseof + | @isarray + | @arrayrank + | @arrayextent + | @isarithmetic + | @iscompletetype + | @iscompound + | @isconst + | @isfloatingpoint + | @isfundamental + | @isintegral + | @islvaluereference + | @ismemberfunctionpointer + | @ismemberobjectpointer + | @ismemberpointer + | @isobject + | @ispointer + | @isreference + | @isrvaluereference + | @isscalar + | @issigned + | @isunsigned + | @isvoid + | @isvolatile + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref, + int position: int ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref, + int position: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/semmlecode.cpp.dbscheme b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..d77c09d8bdc --- /dev/null +++ b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/semmlecode.cpp.dbscheme @@ -0,0 +1,2212 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +case @macroinvocation.kind of + 1 = @macro_expansion +| 2 = @other_macro_reference +; + +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* +case @function.kind of + 1 = @normal_function +| 2 = @constructor +| 3 = @destructor +| 4 = @conversion_function +| 5 = @operator +| 6 = @builtin_function // GCC built-in functions, e.g. __builtin___memcpy_chk +; +*/ + +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point( + int id: @function ref, + unique int entry_point: @stmt ref +); + +function_return_type( + int id: @function ref, + int return_type: @type ref +); + +/** + * If `function` is a coroutine, then this gives the `std::experimental::resumable_traits` + * instance associated with it, and the variables representing the `handle` and `promise` + * for it. + */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type( + unique int id: @function ref, + int this_type: @type ref +); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides( + int new: @function ref, + int old: @function ref +); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +orphaned_variables( + int var: @localvariable ref, + int function: @function ref +) + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/** + * Built-in types are the fundamental types, e.g., integral, floating, and void. + */ +case @builtintype.kind of + 1 = @errortype +| 2 = @unknowntype +| 3 = @void +| 4 = @boolean +| 5 = @char +| 6 = @unsigned_char +| 7 = @signed_char +| 8 = @short +| 9 = @unsigned_short +| 10 = @signed_short +| 11 = @int +| 12 = @unsigned_int +| 13 = @signed_int +| 14 = @long +| 15 = @unsigned_long +| 16 = @signed_long +| 17 = @long_long +| 18 = @unsigned_long_long +| 19 = @signed_long_long +// ... 20 Microsoft-specific __int8 +// ... 21 Microsoft-specific __int16 +// ... 22 Microsoft-specific __int32 +// ... 23 Microsoft-specific __int64 +| 24 = @float +| 25 = @double +| 26 = @long_double +| 27 = @complex_float // C99-specific _Complex float +| 28 = @complex_double // C99-specific _Complex double +| 29 = @complex_long_double // C99-specific _Complex long double +| 30 = @imaginary_float // C99-specific _Imaginary float +| 31 = @imaginary_double // C99-specific _Imaginary double +| 32 = @imaginary_long_double // C99-specific _Imaginary long double +| 33 = @wchar_t // Microsoft-specific +| 34 = @decltype_nullptr // C++11 +| 35 = @int128 // __int128 +| 36 = @unsigned_int128 // unsigned __int128 +| 37 = @signed_int128 // signed __int128 +| 38 = @float128 // __float128 +| 39 = @complex_float128 // _Complex __float128 +| 40 = @decimal32 // _Decimal32 +| 41 = @decimal64 // _Decimal64 +| 42 = @decimal128 // _Decimal128 +| 43 = @char16_t +| 44 = @char32_t +| 45 = @std_float32 // _Float32 +| 46 = @float32x // _Float32x +| 47 = @std_float64 // _Float64 +| 48 = @float64x // _Float64x +| 49 = @std_float128 // _Float128 +// ... 50 _Float128x +| 51 = @char8_t +| 52 = @float16 // _Float16 +| 53 = @complex_float16 // _Complex _Float16 +; + +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/** + * Derived types are types that are directly derived from existing types and + * point to, refer to, transform type data to return a new type. + */ +case @derivedtype.kind of + 1 = @pointer +| 2 = @reference +| 3 = @type_with_specifiers +| 4 = @array +| 5 = @gnu_vector +| 6 = @routineptr +| 7 = @routinereference +| 8 = @rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated +| 10 = @block +; + +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* +case @usertype.kind of + 1 = @struct +| 2 = @class +| 3 = @union +| 4 = @enum +| 5 = @typedef // classic C: typedef typedef type name +| 6 = @template +| 7 = @template_parameter +| 8 = @template_template_parameter +| 9 = @proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated +| 13 = @scoped_enum +| 14 = @using_alias // a using name = type style typedef +; +*/ + +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the frontend. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +| 4 = @attribute_arg_constant_expr +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_constant( + unique int arg: @attribute_arg ref, + int constant: @expr ref +) +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall( + unique int caller: @funbindexpr ref, + int kind: int ref +); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // frontend internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +| 335 = @blockassignexpr +| 336 = @issame +| 337 = @isfunction +| 338 = @islayoutcompatible +| 339 = @ispointerinterconvertiblebaseof +| 340 = @isarray +| 341 = @arrayrank +| 342 = @arrayextent +| 343 = @isarithmetic +| 344 = @iscompletetype +| 345 = @iscompound +| 346 = @isconst +| 347 = @isfloatingpoint +| 348 = @isfundamental +| 349 = @isintegral +| 350 = @islvaluereference +| 351 = @ismemberfunctionpointer +| 352 = @ismemberobjectpointer +| 353 = @ismemberpointer +| 354 = @isobject +| 355 = @ispointer +| 356 = @isreference +| 357 = @isrvaluereference +| 358 = @isscalar +| 359 = @issigned +| 360 = @isunsigned +| 361 = @isvoid +| 362 = @isvolatile +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + | @issame + | @isfunction + | @islayoutcompatible + | @ispointerinterconvertiblebaseof + | @isarray + | @arrayrank + | @arrayextent + | @isarithmetic + | @iscompletetype + | @iscompound + | @isconst + | @isfloatingpoint + | @isfundamental + | @isintegral + | @islvaluereference + | @ismemberfunctionpointer + | @ismemberobjectpointer + | @ismemberpointer + | @isobject + | @ispointer + | @isreference + | @isrvaluereference + | @isscalar + | @issigned + | @isunsigned + | @isvoid + | @isvolatile + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref, + int position: int ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. Position is used to sort repeated initializers. + */ +#keyset[aggregate, position] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref, + int position: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/upgrade.properties b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/upgrade.properties new file mode 100644 index 00000000000..58fffa7cbbc --- /dev/null +++ b/cpp/ql/lib/upgrades/19887dbd33327fb07d54251786e0cb2578539775/upgrade.properties @@ -0,0 +1,3 @@ +description: Remove _Float128 type +compatibility: partial +builtintypes.rel: run builtintypes.qlo diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index 3527a0fc497..dd21d08e8c7 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.2 + +No user-facing changes. + ## 0.7.1 ### Minor Analysis Improvements diff --git a/cpp/ql/src/Critical/UseAfterFree.cpp b/cpp/ql/src/Critical/UseAfterFree.cpp index da921a6a31a..c4d59fa8a9b 100644 --- a/cpp/ql/src/Critical/UseAfterFree.cpp +++ b/cpp/ql/src/Critical/UseAfterFree.cpp @@ -1,9 +1,10 @@ -int f() { +void f() { char* buf = new char[SIZE]; - .... + ... if (error) { - free(buf); //error handling has freed the buffer + delete buf; //error handling has freed the buffer } ... log_contents(buf); //but it is still used here for logging + ... } diff --git a/cpp/ql/src/change-notes/released/0.7.2.md b/cpp/ql/src/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8693d609ec7 --- /dev/null +++ b/cpp/ql/src/change-notes/released/0.7.2.md @@ -0,0 +1,3 @@ +## 0.7.2 + +No user-facing changes. diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql index 93cac5939d4..f778b4f776f 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -15,6 +15,51 @@ * external/cwe/cwe-787 */ +/* + * High-level description of the query: + * + * The goal of this query is to identify issues such as: + * ```cpp + * 1. int* base = new int[size]; + * 2. int* end = base + size; + * 3. for(int* p = base; p <= end; ++p) { + * 4. *p = 0; // BUG: Should have been bounded by `p < end`. + * 5. } + * ``` + * In order to do this, we split the problem into three subtasks: + * 1. First, we find flow from `new int[size]` to `base + size`. + * 2. Then, we find flow from `base + size` to `end` (on line 3). + * 3. Finally, we use range-analysis to find a write to (or read from) a pointer that may be greater than or equal to `end`. + * + * Step 1 is implemented in `AllocationToInvalidPointer.qll`, and step 2 is implemented by + * `InvalidPointerToDereference.qll`. See those files for the description of these. + * + * This file imports both libraries and defines a final dataflow configuration that constructs the full path from + * the allocation to the dereference of the out-of-bounds pointer. This is done for several reasons: + * 1. It means the user is able to inspect the entire path from the allocation to the dereference, which can be useful + * to understand the problem highlighted. + * 2. It ensures that the call-contexts line up correctly when we transition from step 1 to step 2. See the + * `test_missing_call_context_1` and `test_missing_call_context_2` tests for how this may flag false positives + * without this final configuration. + * + * The source of the final path is an allocation that is: + * 1. identified as flowing to an invalid pointer (by `AllocationToInvalidPointer`), and + * 2. for which the invalid pointer flows to a dereference (as identified by `InvalidPointerToDereference`). + * + * The path can be described in 3 "chunks": + * 1. One path from the allocation to the construction of the invalid pointer + * 2. Another path from the construction of the invalid pointer to the final pointer that is about to be dereferenced. + * 3. Finally, a single step from the dataflow node that represents the final pointer to the dereference. + * + * Step 1 happens when the flow state is `TInitial`, and step 2 and 3 happen when the flow state is `TPointerArith(pai)` + * where the pointer-arithmetic instruction `pai` tracks the instruction that generated the out-of-bounds pointer. This + * instruction is used in the construction of the alert message. + * + * The set of pointer-arithmetic instructions that define the `TPointerArith` flow state is restricted to be the pointer- + * arithmetic instructions that both receive flow from the allocation (as identified by `AllocationToInvalidPointer.qll`), + * and further flow to a dereference (as identified by `InvalidPointerToDereference.qll`). + */ + import cpp import semmle.code.cpp.dataflow.new.DataFlow import semmle.code.cpp.ir.IR diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index c2818af35c3..518bf7877cc 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.7.1 +version: 0.7.2 groups: - cpp - queries diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected index 68ade61e255..8de5ea5b9b0 100644 --- a/cpp/ql/test/examples/expressions/PrintAST.expected +++ b/cpp/ql/test/examples/expressions/PrintAST.expected @@ -1308,6 +1308,11 @@ union_etc.cpp: # 6| Type = [IntType] int # 6| ValueCategory = prvalue(load) # 6| getStmt(1): [ReturnStmt] return ... +# 7| [GlobalVariable] S s +# 7| getInitializer(): [Initializer] initializer for s +# 7| getExpr(): [ConstructorCall] call to S +# 7| Type = [VoidType] void +# 7| ValueCategory = prvalue # 9| [CopyAssignmentOperator] C& C::operator=(C const&) # 9| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) @@ -1332,6 +1337,7 @@ union_etc.cpp: # 12| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) #-----| Type = [RValueReferenceType] U && +# 14| [GlobalVariable] C c # 16| [CopyAssignmentOperator] U& U::operator=(U const&) # 16| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) @@ -1356,6 +1362,7 @@ union_etc.cpp: # 18| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) #-----| Type = [RValueReferenceType] C && +# 20| [GlobalVariable] U u # 22| [TopLevelFunction] int foo() # 22| : # 22| getEntryPoint(): [BlockStmt] { ... } diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected index 881a1d8383d..09ec397861a 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected @@ -221,6 +221,13 @@ edges | test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... | | test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... | | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... | +| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... | +| test.cpp:698:5:698:10 | ... += ... | test.cpp:701:15:701:16 | * ... | +| test.cpp:705:18:705:18 | q | test.cpp:705:18:705:18 | q | +| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... | +| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... | +| test.cpp:711:13:711:26 | new[] | test.cpp:714:11:714:11 | q | +| test.cpp:714:11:714:11 | q | test.cpp:705:18:705:18 | q | nodes | test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc | | test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... | @@ -370,6 +377,14 @@ nodes | test.cpp:662:3:662:11 | ... = ... | semmle.label | ... = ... | | test.cpp:667:14:667:31 | new[] | semmle.label | new[] | | test.cpp:675:7:675:23 | ... = ... | semmle.label | ... = ... | +| test.cpp:695:13:695:26 | new[] | semmle.label | new[] | +| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... | +| test.cpp:701:15:701:16 | * ... | semmle.label | * ... | +| test.cpp:705:18:705:18 | q | semmle.label | q | +| test.cpp:705:18:705:18 | q | semmle.label | q | +| test.cpp:706:12:706:13 | * ... | semmle.label | * ... | +| test.cpp:711:13:711:26 | new[] | semmle.label | new[] | +| test.cpp:714:11:714:11 | q | semmle.label | q | subpaths #select | test.cpp:6:14:6:15 | * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size | @@ -405,3 +420,5 @@ subpaths | test.cpp:647:5:647:19 | ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos | | test.cpp:662:3:662:11 | ... = ... | test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:652:14:652:27 | new[] | new[] | test.cpp:653:19:653:22 | size | size | | test.cpp:675:7:675:23 | ... = ... | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:667:14:667:31 | new[] | new[] | test.cpp:675:10:675:18 | ... ++ | ... ++ | +| test.cpp:701:15:701:16 | * ... | test.cpp:695:13:695:26 | new[] | test.cpp:701:15:701:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:695:13:695:26 | new[] | new[] | test.cpp:696:19:696:22 | size | size | +| test.cpp:706:12:706:13 | * ... | test.cpp:711:13:711:26 | new[] | test.cpp:706:12:706:13 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:711:13:711:26 | new[] | new[] | test.cpp:712:19:712:22 | size | size | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp index cfcbb9de9a5..439de8c1749 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp @@ -689,4 +689,28 @@ void test_missing_call_context_2(unsigned size) { int* p = new int[size]; int* end_minus_one = pointer_arithmetic(p, size - 1); *end_minus_one = '0'; // $ deref=L680->L690->L691 // GOOD -} \ No newline at end of file +} + +void test34(unsigned size) { + char *p = new char[size]; + char *end = p + size + 1; // $ alloc=L695 + if (p + 1 < end) { + p += 1; + } + if (p + 1 < end) { + int val = *p; // $ deref=L698->L700->L701 // GOOD [FALSE POSITIVE] + } +} + +void deref(char* q) { + char x = *q; // $ deref=L714->L705->L706 // BAD +} + +void test35(unsigned long size, char* q) +{ + char* p = new char[size]; + char* end = p + size; // $ alloc=L711 + if(q <= end) { + deref(q); + } +} diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp index 5952529e93e..e00523a36cf 100644 --- a/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp +++ b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp @@ -16,3 +16,5 @@ class AddressOfGetter { &field; } }; + +__declspec("SAL_volatile") char* pBuf; diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected index 501d699758a..376224b167d 100644 --- a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected +++ b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected @@ -4,6 +4,7 @@ | ms_var_attributes.cpp:8:15:8:20 | myInt4 | ms_var_attributes.cpp:8:1:8:9 | dllexport | | ms_var_attributes.cpp:9:5:9:10 | myInt5 | ms_var_attributes.h:7:1:7:9 | dllexport | | ms_var_attributes.cpp:12:42:12:46 | field | ms_var_attributes.cpp:12:14:12:21 | property | +| ms_var_attributes.cpp:20:34:20:37 | pBuf | ms_var_attributes.cpp:20:12:20:12 | SAL_volatile | | ms_var_attributes.h:5:22:5:27 | myInt3 | ms_var_attributes.h:5:1:5:9 | dllexport | | var_attributes.c:1:12:1:19 | weak_var | var_attributes.c:1:36:1:39 | weak | | var_attributes.c:2:12:2:22 | weakref_var | var_attributes.c:2:39:2:45 | weakref | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index fe7b07a6ec9..ba76826993d 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -1728,6 +1728,76 @@ complex.c: # 144| Type = [LongDoubleType] long double # 144| ValueCategory = prvalue # 145| getStmt(72): [ReturnStmt] return ... +ir.c: +# 5| [TopLevelFunction] int getX(MyCoords*) +# 5| : +# 5| getParameter(0): [Parameter] coords +# 5| Type = [PointerType] MyCoords * +# 7| [TopLevelFunction] void MyCoordsTest(int) +# 7| : +# 7| getParameter(0): [Parameter] pos +# 7| Type = [IntType] int +# 7| getEntryPoint(): [BlockStmt] { ... } +# 8| getStmt(0): [DeclStmt] declaration +# 8| getDeclarationEntry(0): [VariableDeclarationEntry] definition of coords +# 8| Type = [CTypedefType] MyCoords +# 8| getVariable().getInitializer(): [Initializer] initializer for coords +# 8| getExpr(): [ClassAggregateLiteral] {...} +# 8| Type = [Struct] (unnamed class/struct/union) +# 8| ValueCategory = prvalue +# 8| getAFieldExpr(x): [Literal] 0 +# 8| Type = [IntType] int +# 8| Value = [Literal] 0 +# 8| ValueCategory = prvalue +# 9| getStmt(1): [ExprStmt] ExprStmt +# 9| getExpr(): [AssignExpr] ... = ... +# 9| Type = [IntType] int +# 9| ValueCategory = prvalue +# 9| getLValue(): [ValueFieldAccess] x +# 9| Type = [IntType] int +# 9| ValueCategory = lvalue +# 9| getQualifier(): [VariableAccess] coords +# 9| Type = [CTypedefType] MyCoords +# 9| ValueCategory = lvalue +# 9| getRValue(): [AssignExpr] ... = ... +# 9| Type = [IntType] int +# 9| ValueCategory = prvalue +# 9| getLValue(): [ValueFieldAccess] y +# 9| Type = [IntType] int +# 9| ValueCategory = lvalue +# 9| getQualifier(): [VariableAccess] coords +# 9| Type = [CTypedefType] MyCoords +# 9| ValueCategory = lvalue +# 9| getRValue(): [AddExpr] ... + ... +# 9| Type = [IntType] int +# 9| ValueCategory = prvalue +# 9| getLeftOperand(): [VariableAccess] pos +# 9| Type = [IntType] int +# 9| ValueCategory = prvalue(load) +# 9| getRightOperand(): [Literal] 1 +# 9| Type = [IntType] int +# 9| Value = [Literal] 1 +# 9| ValueCategory = prvalue +# 10| getStmt(2): [ExprStmt] ExprStmt +# 10| getExpr(): [AssignExpr] ... = ... +# 10| Type = [IntType] int +# 10| ValueCategory = prvalue +# 10| getLValue(): [ValueFieldAccess] x +# 10| Type = [IntType] int +# 10| ValueCategory = lvalue +# 10| getQualifier(): [VariableAccess] coords +# 10| Type = [CTypedefType] MyCoords +# 10| ValueCategory = lvalue +# 10| getRValue(): [FunctionCall] call to getX +# 10| Type = [IntType] int +# 10| ValueCategory = prvalue +# 10| getArgument(0): [AddressOfExpr] & ... +# 10| Type = [PointerType] MyCoords * +# 10| ValueCategory = prvalue +# 10| getOperand(): [VariableAccess] coords +# 10| Type = [CTypedefType] MyCoords +# 10| ValueCategory = lvalue +# 11| getStmt(3): [ReturnStmt] return ... ir.cpp: # 1| [TopLevelFunction] void Constants() # 1| : @@ -8529,6 +8599,11 @@ ir.cpp: # 1035| Type = [Struct] EmptyStruct # 1035| ValueCategory = prvalue # 1036| getStmt(1): [ReturnStmt] return ... +# 1038| [GlobalVariable] (lambda [] type at line 1038, col. 12) lam +# 1038| getInitializer(): [Initializer] initializer for lam +# 1038| getExpr(): [LambdaExpression] [...](...){...} +# 1038| Type = [Closure] decltype([...](...){...}) +# 1038| ValueCategory = prvalue # 1038| [CopyAssignmentOperator] (lambda [] type at line 1038, col. 12)& (lambda [] type at line 1038, col. 12)::operator=((lambda [] type at line 1038, col. 12) const&) # 1038| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) @@ -13976,6 +14051,54 @@ ir.cpp: # 1815| Type = [IntType] int # 1815| ValueCategory = prvalue(load) # 1817| getStmt(8): [ReturnStmt] return ... +# 1821| [GlobalVariable] int global_2 +# 1821| getInitializer(): [Initializer] initializer for global_2 +# 1821| getExpr(): [Literal] 1 +# 1821| Type = [IntType] int +# 1821| Value = [Literal] 1 +# 1821| ValueCategory = prvalue +# 1823| [GlobalVariable] int const global_3 +# 1823| getInitializer(): [Initializer] initializer for global_3 +# 1823| getExpr(): [Literal] 2 +# 1823| Type = [IntType] int +# 1823| Value = [Literal] 2 +# 1823| ValueCategory = prvalue +# 1825| [GlobalVariable] constructor_only global_4 +# 1825| getInitializer(): [Initializer] initializer for global_4 +# 1825| getExpr(): [ConstructorCall] call to constructor_only +# 1825| Type = [VoidType] void +# 1825| ValueCategory = prvalue +# 1825| getArgument(0): [Literal] 1 +# 1825| Type = [IntType] int +# 1825| Value = [Literal] 1 +# 1825| ValueCategory = prvalue +# 1827| [GlobalVariable] constructor_only global_5 +# 1827| getInitializer(): [Initializer] initializer for global_5 +# 1827| getExpr(): [ConstructorCall] call to constructor_only +# 1827| Type = [VoidType] void +# 1827| ValueCategory = prvalue +# 1827| getArgument(0): [Literal] 2 +# 1827| Type = [IntType] int +# 1827| Value = [Literal] 2 +# 1827| ValueCategory = prvalue +# 1829| [GlobalVariable] char* global_string +# 1829| getInitializer(): [Initializer] initializer for global_string +# 1829| getExpr(): global string +# 1829| Type = [ArrayType] const char[14] +# 1829| Value = [StringLiteral] "global string" +# 1829| ValueCategory = lvalue +# 1829| getExpr().getFullyConverted(): [CStyleCast] (char *)... +# 1829| Conversion = [PointerConversion] pointer conversion +# 1829| Type = [CharPointerType] char * +# 1829| ValueCategory = prvalue +# 1829| getExpr(): [ArrayToPointerConversion] array to pointer conversion +# 1829| Type = [PointerType] const char * +# 1829| ValueCategory = prvalue +# 1831| [GlobalVariable] int global_6 +# 1831| getInitializer(): [Initializer] initializer for global_6 +# 1831| getExpr(): [VariableAccess] global_2 +# 1831| Type = [IntType] int +# 1831| ValueCategory = prvalue(load) # 1834| [CopyAssignmentOperator] block_assignment::A& block_assignment::A::operator=(block_assignment::A const&) # 1834| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) @@ -14377,6 +14500,23 @@ ir.cpp: # 1885| Type = [ClassTemplateInstantiation,Struct] Bar2 # 1885| ValueCategory = lvalue # 1886| getStmt(2): [ReturnStmt] return ... +# 1889| [GlobalVariable] char global_template +# 1889| getInitializer(): [Initializer] initializer for global_template +# 1889| getExpr(): [Literal] 42 +# 1889| Type = [IntType] int +# 1889| Value = [Literal] 42 +# 1889| ValueCategory = prvalue +# 1889| getExpr().getFullyConverted(): [CStyleCast] (char)... +# 1889| Conversion = [IntegralConversion] integral conversion +# 1889| Type = [PlainCharType] char +# 1889| Value = [CStyleCast] 42 +# 1889| ValueCategory = prvalue +# 1889| [GlobalVariable] int global_template +# 1889| getInitializer(): [Initializer] initializer for global_template +# 1889| getExpr(): [Literal] 42 +# 1889| Type = [IntType] int +# 1889| Value = [Literal] 42 +# 1889| ValueCategory = prvalue # 1891| [TopLevelFunction] int test_global_template_int() # 1891| : # 1891| getEntryPoint(): [BlockStmt] { ... } @@ -14888,6 +15028,514 @@ ir.cpp: # 1993| Type = [Class] C # 1993| ValueCategory = lvalue # 1994| getStmt(3): [ReturnStmt] return ... +# 1996| [TopLevelFunction] void TernaryTestInt(bool, int, int, int) +# 1996| : +# 1996| getParameter(0): [Parameter] a +# 1996| Type = [BoolType] bool +# 1996| getParameter(1): [Parameter] x +# 1996| Type = [IntType] int +# 1996| getParameter(2): [Parameter] y +# 1996| Type = [IntType] int +# 1996| getParameter(3): [Parameter] z +# 1996| Type = [IntType] int +# 1996| getEntryPoint(): [BlockStmt] { ... } +# 1997| getStmt(0): [ExprStmt] ExprStmt +# 1997| getExpr(): [AssignExpr] ... = ... +# 1997| Type = [IntType] int +# 1997| ValueCategory = lvalue +# 1997| getLValue(): [VariableAccess] z +# 1997| Type = [IntType] int +# 1997| ValueCategory = lvalue +# 1997| getRValue(): [ConditionalExpr] ... ? ... : ... +# 1997| Type = [IntType] int +# 1997| ValueCategory = prvalue +# 1997| getCondition(): [VariableAccess] a +# 1997| Type = [BoolType] bool +# 1997| ValueCategory = prvalue(load) +# 1997| getThen(): [VariableAccess] x +# 1997| Type = [IntType] int +# 1997| ValueCategory = prvalue(load) +# 1997| getElse(): [VariableAccess] y +# 1997| Type = [IntType] int +# 1997| ValueCategory = prvalue(load) +# 1998| getStmt(1): [ExprStmt] ExprStmt +# 1998| getExpr(): [AssignExpr] ... = ... +# 1998| Type = [IntType] int +# 1998| ValueCategory = lvalue +# 1998| getLValue(): [VariableAccess] z +# 1998| Type = [IntType] int +# 1998| ValueCategory = lvalue +# 1998| getRValue(): [ConditionalExpr] ... ? ... : ... +# 1998| Type = [IntType] int +# 1998| ValueCategory = prvalue +# 1998| getCondition(): [VariableAccess] a +# 1998| Type = [BoolType] bool +# 1998| ValueCategory = prvalue(load) +# 1998| getThen(): [VariableAccess] x +# 1998| Type = [IntType] int +# 1998| ValueCategory = prvalue(load) +# 1998| getElse(): [Literal] 5 +# 1998| Type = [IntType] int +# 1998| Value = [Literal] 5 +# 1998| ValueCategory = prvalue +# 1999| getStmt(2): [ExprStmt] ExprStmt +# 1999| getExpr(): [AssignExpr] ... = ... +# 1999| Type = [IntType] int +# 1999| ValueCategory = lvalue +# 1999| getLValue(): [VariableAccess] z +# 1999| Type = [IntType] int +# 1999| ValueCategory = lvalue +# 1999| getRValue(): [ConditionalExpr] ... ? ... : ... +# 1999| Type = [IntType] int +# 1999| ValueCategory = prvalue +# 1999| getCondition(): [VariableAccess] a +# 1999| Type = [BoolType] bool +# 1999| ValueCategory = prvalue(load) +# 1999| getThen(): [Literal] 3 +# 1999| Type = [IntType] int +# 1999| Value = [Literal] 3 +# 1999| ValueCategory = prvalue +# 1999| getElse(): [Literal] 5 +# 1999| Type = [IntType] int +# 1999| Value = [Literal] 5 +# 1999| ValueCategory = prvalue +# 2000| getStmt(3): [ExprStmt] ExprStmt +# 2000| getExpr(): [AssignExpr] ... = ... +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue +# 2000| getLValue(): [ConditionalExpr] ... ? ... : ... +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue +# 2000| getCondition(): [VariableAccess] a +# 2000| Type = [BoolType] bool +# 2000| ValueCategory = prvalue(load) +# 2000| getThen(): [VariableAccess] x +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue +# 2000| getElse(): [VariableAccess] y +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue +# 2000| getRValue(): [Literal] 7 +# 2000| Type = [IntType] int +# 2000| Value = [Literal] 7 +# 2000| ValueCategory = prvalue +# 2000| getLValue().getFullyConverted(): [ParenthesisExpr] (...) +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue +# 2001| getStmt(4): [ReturnStmt] return ... +# 2003| [CopyAssignmentOperator] TernaryPodObj& TernaryPodObj::operator=(TernaryPodObj const&) +# 2003| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [LValueReferenceType] const TernaryPodObj & +# 2003| [MoveAssignmentOperator] TernaryPodObj& TernaryPodObj::operator=(TernaryPodObj&&) +# 2003| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [RValueReferenceType] TernaryPodObj && +# 2006| [TopLevelFunction] void TernaryTestPodObj(bool, TernaryPodObj, TernaryPodObj, TernaryPodObj) +# 2006| : +# 2006| getParameter(0): [Parameter] a +# 2006| Type = [BoolType] bool +# 2006| getParameter(1): [Parameter] x +# 2006| Type = [Struct] TernaryPodObj +# 2006| getParameter(2): [Parameter] y +# 2006| Type = [Struct] TernaryPodObj +# 2006| getParameter(3): [Parameter] z +# 2006| Type = [Struct] TernaryPodObj +# 2006| getEntryPoint(): [BlockStmt] { ... } +# 2007| getStmt(0): [ExprStmt] ExprStmt +# 2007| getExpr(): [AssignExpr] ... = ... +# 2007| Type = [Struct] TernaryPodObj +# 2007| ValueCategory = lvalue +# 2007| getLValue(): [VariableAccess] z +# 2007| Type = [Struct] TernaryPodObj +# 2007| ValueCategory = lvalue +# 2007| getRValue(): [ConditionalExpr] ... ? ... : ... +# 2007| Type = [Struct] TernaryPodObj +# 2007| ValueCategory = prvalue +# 2007| getCondition(): [VariableAccess] a +# 2007| Type = [BoolType] bool +# 2007| ValueCategory = prvalue(load) +# 2007| getThen(): [VariableAccess] x +# 2007| Type = [Struct] TernaryPodObj +# 2007| ValueCategory = prvalue(load) +# 2007| getElse(): [VariableAccess] y +# 2007| Type = [Struct] TernaryPodObj +# 2007| ValueCategory = prvalue(load) +# 2008| getStmt(1): [ExprStmt] ExprStmt +# 2008| getExpr(): [AssignExpr] ... = ... +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = lvalue +# 2008| getLValue(): [VariableAccess] z +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = lvalue +# 2008| getRValue(): [ConditionalExpr] ... ? ... : ... +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = prvalue +# 2008| getCondition(): [VariableAccess] a +# 2008| Type = [BoolType] bool +# 2008| ValueCategory = prvalue(load) +# 2008| getThen(): [VariableAccess] x +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = prvalue(load) +# 2008| getElse(): [Literal] 0 +# 2008| Type = [Struct] TernaryPodObj +# 2008| Value = [Literal] 0 +# 2008| ValueCategory = prvalue +# 2008| getThen().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = prvalue(load) +# 2008| getElse().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = prvalue(load) +# 2008| getRValue().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2008| Type = [Struct] TernaryPodObj +# 2008| ValueCategory = prvalue(load) +# 2009| getStmt(2): [ExprStmt] ExprStmt +# 2009| getExpr(): [AssignExpr] ... = ... +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = lvalue +# 2009| getLValue(): [VariableAccess] z +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = lvalue +# 2009| getRValue(): [ConditionalExpr] ... ? ... : ... +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = prvalue +# 2009| getCondition(): [VariableAccess] a +# 2009| Type = [BoolType] bool +# 2009| ValueCategory = prvalue(load) +# 2009| getThen(): [Literal] 0 +# 2009| Type = [Struct] TernaryPodObj +# 2009| Value = [Literal] 0 +# 2009| ValueCategory = prvalue +# 2009| getElse(): [Literal] 0 +# 2009| Type = [Struct] TernaryPodObj +# 2009| Value = [Literal] 0 +# 2009| ValueCategory = prvalue +# 2009| getThen().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = prvalue(load) +# 2009| getElse().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = prvalue(load) +# 2009| getRValue().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2009| Type = [Struct] TernaryPodObj +# 2009| ValueCategory = prvalue(load) +# 2010| getStmt(3): [ExprStmt] ExprStmt +# 2010| getExpr(): [AssignExpr] ... = ... +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = lvalue +# 2010| getLValue(): [AssignExpr] ... = ... +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = lvalue +# 2010| getLValue(): [VariableAccess] z +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = lvalue +# 2010| getRValue(): [ConditionalExpr] ... ? ... : ... +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue +# 2010| getCondition(): [VariableAccess] a +# 2010| Type = [BoolType] bool +# 2010| ValueCategory = prvalue(load) +# 2010| getThen(): [VariableAccess] x +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue(load) +# 2010| getElse(): [VariableAccess] y +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue(load) +# 2010| getRValue(): [Literal] 0 +# 2010| Type = [Struct] TernaryPodObj +# 2010| Value = [Literal] 0 +# 2010| ValueCategory = prvalue +# 2010| getLValue().getFullyConverted(): [ParenthesisExpr] (...) +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = lvalue +# 2010| getRValue().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue(load) +# 2011| getStmt(4): [ReturnStmt] return ... +# 2013| [CopyAssignmentOperator] TernaryNonPodObj& TernaryNonPodObj::operator=(TernaryNonPodObj const&) +# 2013| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [LValueReferenceType] const TernaryNonPodObj & +#-----| getEntryPoint(): [BlockStmt] { ... } +#-----| getStmt(0): [ReturnStmt] return ... +#-----| getExpr(): [PointerDereferenceExpr] * ... +#-----| Type = [Struct] TernaryNonPodObj +#-----| ValueCategory = lvalue +#-----| getOperand(): [ThisExpr] this +#-----| Type = [PointerType] TernaryNonPodObj * +#-----| ValueCategory = prvalue(load) +#-----| getExpr().getFullyConverted(): [ReferenceToExpr] (reference to) +#-----| Type = [LValueReferenceType] TernaryNonPodObj & +#-----| ValueCategory = prvalue +# 2013| [Constructor] void TernaryNonPodObj::TernaryNonPodObj() +# 2013| : +# 2013| : +# 2013| getEntryPoint(): [BlockStmt] { ... } +# 2013| getStmt(0): [ReturnStmt] return ... +# 2013| [CopyConstructor] void TernaryNonPodObj::TernaryNonPodObj(TernaryNonPodObj const&) +# 2013| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2013| : +# 2013| getEntryPoint(): [BlockStmt] { ... } +# 2013| getStmt(0): [ReturnStmt] return ... +# 2014| [Destructor,VirtualFunction] void TernaryNonPodObj::~TernaryNonPodObj() +# 2014| : +# 2014| getEntryPoint(): [BlockStmt] { ... } +# 2014| getStmt(0): [ReturnStmt] return ... +# 2014| : +# 2017| [TopLevelFunction] void TernaryTestNonPodObj(bool, TernaryNonPodObj, TernaryNonPodObj, TernaryNonPodObj) +# 2017| : +# 2017| getParameter(0): [Parameter] a +# 2017| Type = [BoolType] bool +# 2017| getParameter(1): [Parameter] x +# 2017| Type = [Struct] TernaryNonPodObj +# 2017| getParameter(2): [Parameter] y +# 2017| Type = [Struct] TernaryNonPodObj +# 2017| getParameter(3): [Parameter] z +# 2017| Type = [Struct] TernaryNonPodObj +# 2017| getEntryPoint(): [BlockStmt] { ... } +# 2018| getStmt(0): [ExprStmt] ExprStmt +# 2018| getExpr(): [FunctionCall] call to operator= +# 2018| Type = [LValueReferenceType] TernaryNonPodObj & +# 2018| ValueCategory = prvalue +# 2018| getQualifier(): [VariableAccess] z +# 2018| Type = [Struct] TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2018| getArgument(0): [ConditionalExpr] ... ? ... : ... +# 2018| Type = [Struct] TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2018| getCondition(): [VariableAccess] a +# 2018| Type = [BoolType] bool +# 2018| ValueCategory = prvalue(load) +# 2018| getThen(): [VariableAccess] x +# 2018| Type = [Struct] TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2018| getElse(): [VariableAccess] y +# 2018| Type = [Struct] TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2018| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2018| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2018| ValueCategory = prvalue +# 2018| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2018| Conversion = [GlvalueConversion] glvalue conversion +# 2018| Type = [SpecifiedType] const TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2018| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) +# 2018| Type = [Struct] TernaryNonPodObj +# 2018| ValueCategory = lvalue +# 2019| getStmt(1): [ExprStmt] ExprStmt +# 2019| getExpr(): [FunctionCall] call to operator= +# 2019| Type = [LValueReferenceType] TernaryNonPodObj & +# 2019| ValueCategory = prvalue +# 2019| getQualifier(): [VariableAccess] z +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2019| getArgument(0): [ConditionalExpr] ... ? ... : ... +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = prvalue +# 2019| getCondition(): [VariableAccess] a +# 2019| Type = [BoolType] bool +# 2019| ValueCategory = prvalue(load) +# 2019| getThen(): [ConstructorCall] call to TernaryNonPodObj +# 2019| Type = [VoidType] void +# 2019| ValueCategory = prvalue +# 2019| getArgument(0): [VariableAccess] x +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2019| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2019| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2019| ValueCategory = prvalue +# 2019| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2019| Conversion = [GlvalueConversion] glvalue conversion +# 2019| Type = [SpecifiedType] const TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2019| getElse(): [ConstructorCall] call to TernaryNonPodObj +# 2019| Type = [VoidType] void +# 2019| ValueCategory = prvalue +# 2019| getThen().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = prvalue(load) +# 2019| getElse().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = prvalue(load) +# 2019| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2019| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2019| ValueCategory = prvalue +# 2019| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2019| Conversion = [GlvalueConversion] glvalue conversion +# 2019| Type = [SpecifiedType] const TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2019| getExpr(): [TemporaryObjectExpr] temporary object +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2019| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) +# 2019| Type = [Struct] TernaryNonPodObj +# 2019| ValueCategory = lvalue +# 2020| getStmt(2): [ExprStmt] ExprStmt +# 2020| getExpr(): [FunctionCall] call to operator= +# 2020| Type = [LValueReferenceType] TernaryNonPodObj & +# 2020| ValueCategory = prvalue +# 2020| getQualifier(): [VariableAccess] z +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = lvalue +# 2020| getArgument(0): [ConditionalExpr] ... ? ... : ... +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = prvalue +# 2020| getCondition(): [VariableAccess] a +# 2020| Type = [BoolType] bool +# 2020| ValueCategory = prvalue(load) +# 2020| getThen(): [ConstructorCall] call to TernaryNonPodObj +# 2020| Type = [VoidType] void +# 2020| ValueCategory = prvalue +# 2020| getElse(): [ConstructorCall] call to TernaryNonPodObj +# 2020| Type = [VoidType] void +# 2020| ValueCategory = prvalue +# 2020| getThen().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = prvalue(load) +# 2020| getElse().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = prvalue(load) +# 2020| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2020| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2020| ValueCategory = prvalue +# 2020| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2020| Conversion = [GlvalueConversion] glvalue conversion +# 2020| Type = [SpecifiedType] const TernaryNonPodObj +# 2020| ValueCategory = lvalue +# 2020| getExpr(): [TemporaryObjectExpr] temporary object +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = lvalue +# 2020| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) +# 2020| Type = [Struct] TernaryNonPodObj +# 2020| ValueCategory = lvalue +# 2021| getStmt(3): [ExprStmt] ExprStmt +# 2021| getExpr(): [FunctionCall] call to operator= +# 2021| Type = [LValueReferenceType] TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getQualifier(): [FunctionCall] call to operator= +# 2021| Type = [LValueReferenceType] TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getQualifier(): [VariableAccess] z +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0): [ConditionalExpr] ... ? ... : ... +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getCondition(): [VariableAccess] a +# 2021| Type = [BoolType] bool +# 2021| ValueCategory = prvalue(load) +# 2021| getThen(): [VariableAccess] x +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getElse(): [VariableAccess] y +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2021| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2021| Conversion = [GlvalueConversion] glvalue conversion +# 2021| Type = [SpecifiedType] const TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0): [ConstructorCall] call to TernaryNonPodObj +# 2021| Type = [VoidType] void +# 2021| ValueCategory = prvalue +# 2021| getQualifier().getFullyConverted(): [ParenthesisExpr] (...) +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getExpr(): [ReferenceDereferenceExpr] (reference dereference) +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2021| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2021| Conversion = [GlvalueConversion] glvalue conversion +# 2021| Type = [SpecifiedType] const TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getExpr(): [TemporaryObjectExpr] temporary object +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2022| getStmt(4): [ReturnStmt] return ... +# 2024| [TopLevelFunction] void CommaTestHelper(unsigned int) +# 2024| : +# 2024| getParameter(0): [Parameter] (unnamed parameter 0) +# 2024| Type = [IntType] unsigned int +# 2026| [TopLevelFunction] unsigned int CommaTest(unsigned int) +# 2026| : +# 2026| getParameter(0): [Parameter] x +# 2026| Type = [IntType] unsigned int +# 2026| getEntryPoint(): [BlockStmt] { ... } +# 2027| getStmt(0): [DeclStmt] declaration +# 2027| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y +# 2027| Type = [IntType] unsigned int +# 2028| getStmt(1): [ExprStmt] ExprStmt +# 2028| getExpr(): [AssignExpr] ... = ... +# 2028| Type = [IntType] unsigned int +# 2028| ValueCategory = lvalue +# 2028| getLValue(): [VariableAccess] y +# 2028| Type = [IntType] unsigned int +# 2028| ValueCategory = lvalue +# 2028| getRValue(): [ConditionalExpr] ... ? ... : ... +# 2028| Type = [IntType] unsigned int +# 2028| ValueCategory = prvalue +# 2028| getCondition(): [LTExpr] ... < ... +# 2028| Type = [BoolType] bool +# 2028| ValueCategory = prvalue +# 2028| getLesserOperand(): [VariableAccess] x +# 2028| Type = [IntType] unsigned int +# 2028| ValueCategory = prvalue(load) +# 2028| getGreaterOperand(): [Literal] 100 +# 2028| Type = [IntType] int +# 2028| Value = [Literal] 100 +# 2028| ValueCategory = prvalue +# 2028| getGreaterOperand().getFullyConverted(): [CStyleCast] (unsigned int)... +# 2028| Conversion = [IntegralConversion] integral conversion +# 2028| Type = [IntType] unsigned int +# 2028| Value = [CStyleCast] 100 +# 2028| ValueCategory = prvalue +# 2029| getThen(): [CommaExpr] ... , ... +# 2029| Type = [IntType] unsigned int +# 2029| ValueCategory = prvalue +# 2029| getLeftOperand(): [FunctionCall] call to CommaTestHelper +# 2029| Type = [VoidType] void +# 2029| ValueCategory = prvalue +# 2029| getArgument(0): [VariableAccess] x +# 2029| Type = [IntType] unsigned int +# 2029| ValueCategory = prvalue(load) +# 2029| getRightOperand(): [VariableAccess] x +# 2029| Type = [IntType] unsigned int +# 2029| ValueCategory = prvalue(load) +# 2030| getElse(): [CommaExpr] ... , ... +# 2030| Type = [IntType] int +# 2030| ValueCategory = prvalue +# 2030| getLeftOperand(): [FunctionCall] call to CommaTestHelper +# 2030| Type = [VoidType] void +# 2030| ValueCategory = prvalue +# 2030| getArgument(0): [VariableAccess] x +# 2030| Type = [IntType] unsigned int +# 2030| ValueCategory = prvalue(load) +# 2030| getRightOperand(): [Literal] 10 +# 2030| Type = [IntType] int +# 2030| Value = [Literal] 10 +# 2030| ValueCategory = prvalue +# 2029| getThen().getFullyConverted(): [ParenthesisExpr] (...) +# 2029| Type = [IntType] unsigned int +# 2029| ValueCategory = prvalue +# 2030| getElse().getFullyConverted(): [CStyleCast] (unsigned int)... +# 2030| Conversion = [IntegralConversion] integral conversion +# 2030| Type = [IntType] unsigned int +# 2030| ValueCategory = prvalue +# 2030| getExpr(): [ParenthesisExpr] (...) +# 2030| Type = [IntType] int +# 2030| ValueCategory = prvalue +# 2031| getStmt(2): [ReturnStmt] return ... perf-regression.cpp: # 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&) # 4| : @@ -15170,6 +15818,40 @@ struct_init.cpp: # 4| : #-----| getParameter(0): [Parameter] (unnamed parameter 0) #-----| Type = [RValueReferenceType] Info && +# 9| [GlobalVariable] Info infos_in_file[] +# 9| getInitializer(): [Initializer] initializer for infos_in_file +# 9| getExpr(): [ArrayAggregateLiteral] {...} +# 9| Type = [ArrayType] Info[2] +# 9| ValueCategory = prvalue +# 10| getAnElementExpr(0): [ClassAggregateLiteral] {...} +# 10| Type = [Struct] Info +# 10| ValueCategory = prvalue +# 10| getAFieldExpr(name): 1 +# 10| Type = [ArrayType] const char[2] +# 10| Value = [StringLiteral] "1" +# 10| ValueCategory = lvalue +# 10| getAFieldExpr(handler): [FunctionAccess] handler1 +# 10| Type = [FunctionPointerType] ..(*)(..) +# 10| ValueCategory = prvalue(load) +# 10| getAFieldExpr(name).getFullyConverted(): [ArrayToPointerConversion] array to pointer conversion +# 10| Type = [PointerType] const char * +# 10| ValueCategory = prvalue +# 11| getAnElementExpr(1): [ClassAggregateLiteral] {...} +# 11| Type = [Struct] Info +# 11| ValueCategory = prvalue +# 11| getAFieldExpr(name): 3 +# 11| Type = [ArrayType] const char[2] +# 11| Value = [StringLiteral] "3" +# 11| ValueCategory = lvalue +# 11| getAFieldExpr(handler): [AddressOfExpr] & ... +# 11| Type = [FunctionPointerType] ..(*)(..) +# 11| ValueCategory = prvalue +# 11| getOperand(): [FunctionAccess] handler2 +# 11| Type = [RoutineType] ..()(..) +# 11| ValueCategory = lvalue +# 11| getAFieldExpr(name).getFullyConverted(): [ArrayToPointerConversion] array to pointer conversion +# 11| Type = [PointerType] const char * +# 11| ValueCategory = prvalue # 16| [TopLevelFunction] void let_info_escape(Info*) # 16| : # 16| getParameter(0): [Parameter] info diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.ql b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql index 471b12ed3e5..03321d9e491 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.ql +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql @@ -7,5 +7,5 @@ private import semmle.code.cpp.PrintAST private import PrintConfig private class PrintConfig extends PrintAstConfiguration { - override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } + override predicate shouldPrintDeclaration(Declaration decl) { shouldDumpDeclaration(decl) } } diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll index 6d3db164900..aa23cf423ad 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -8,11 +8,11 @@ predicate locationIsInStandardHeaders(Location loc) { } /** - * Holds if the AST or IR for the specified function should be printed in the test output. + * Holds if the AST or IR for the specified declaration should be printed in the test output. * - * This predicate excludes functions defined in standard headers. + * This predicate excludes declarations defined in standard headers. */ -predicate shouldDumpFunction(Declaration decl) { +predicate shouldDumpDeclaration(Declaration decl) { not locationIsInStandardHeaders(decl.getLocation()) and ( decl instanceof Function diff --git a/cpp/ql/test/library-tests/ir/ir/ir.c b/cpp/ql/test/library-tests/ir/ir/ir.c new file mode 100644 index 00000000000..c162abc2715 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ir.c @@ -0,0 +1,11 @@ +typedef struct { + int x, y; +} MyCoords; + +int getX(MyCoords *coords); + +void MyCoordsTest(int pos) { + MyCoords coords = {0}; + coords.x = coords.y = pos + 1; + coords.x = getX(&coords); +} diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 73d6c2381c6..44f88d23166 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1993,4 +1993,41 @@ void SetStaticFuncPtr() { pfn = c.StaticMemberFunction; } +void TernaryTestInt(bool a, int x, int y, int z) { + z = a ? x : y; + z = a ? x : 5; + z = a ? 3 : 5; + (a ? x : y) = 7; +} + +struct TernaryPodObj { +}; + +void TernaryTestPodObj(bool a, TernaryPodObj x, TernaryPodObj y, TernaryPodObj z) { + z = a ? x : y; + z = a ? x : TernaryPodObj(); + z = a ? TernaryPodObj() : TernaryPodObj(); + (z = a ? x : y) = TernaryPodObj(); +} + +struct TernaryNonPodObj { + virtual ~TernaryNonPodObj() {} +}; + +void TernaryTestNonPodObj(bool a, TernaryNonPodObj x, TernaryNonPodObj y, TernaryNonPodObj z) { + z = a ? x : y; + z = a ? x : TernaryNonPodObj(); + z = a ? TernaryNonPodObj() : TernaryNonPodObj(); + (z = a ? x : y) = TernaryNonPodObj(); +} + +void CommaTestHelper(unsigned int); + +unsigned int CommaTest(unsigned int x) { + unsigned int y; + y = x < 100 ? + (CommaTestHelper(x), x) : + (CommaTestHelper(x), 10); +} + // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index e8561a7709e..96698243672 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -680,6 +680,10 @@ | file://:0:0:0:0 | Address | &:r0_1 | | file://:0:0:0:0 | Address | &:r0_1 | | file://:0:0:0:0 | Address | &:r0_1 | +| file://:0:0:0:0 | Address | &:r0_1 | +| file://:0:0:0:0 | Address | &:r0_1 | +| file://:0:0:0:0 | Address | &:r0_1 | +| file://:0:0:0:0 | Address | &:r0_1 | | file://:0:0:0:0 | Address | &:r0_2 | | file://:0:0:0:0 | Address | &:r0_3 | | file://:0:0:0:0 | Address | &:r0_3 | @@ -704,6 +708,10 @@ | file://:0:0:0:0 | Address | &:r0_3 | | file://:0:0:0:0 | Address | &:r0_3 | | file://:0:0:0:0 | Address | &:r0_3 | +| file://:0:0:0:0 | Address | &:r0_3 | +| file://:0:0:0:0 | Address | &:r0_3 | +| file://:0:0:0:0 | Address | &:r0_3 | +| file://:0:0:0:0 | Address | &:r0_3 | | file://:0:0:0:0 | Address | &:r0_4 | | file://:0:0:0:0 | Address | &:r0_4 | | file://:0:0:0:0 | Address | &:r0_5 | @@ -715,6 +723,8 @@ | file://:0:0:0:0 | Address | &:r0_5 | | file://:0:0:0:0 | Address | &:r0_5 | | file://:0:0:0:0 | Address | &:r0_5 | +| file://:0:0:0:0 | Address | &:r0_5 | +| file://:0:0:0:0 | Address | &:r0_6 | | file://:0:0:0:0 | Address | &:r0_6 | | file://:0:0:0:0 | Address | &:r0_7 | | file://:0:0:0:0 | Address | &:r0_7 | @@ -799,6 +809,8 @@ | file://:0:0:0:0 | Load | m0_2 | | file://:0:0:0:0 | Load | m0_2 | | file://:0:0:0:0 | Load | m0_2 | +| file://:0:0:0:0 | Load | m0_2 | +| file://:0:0:0:0 | Load | m0_2 | | file://:0:0:0:0 | Load | m0_5 | | file://:0:0:0:0 | Load | m0_8 | | file://:0:0:0:0 | Load | m0_11 | @@ -814,6 +826,7 @@ | file://:0:0:0:0 | Load | m1834_6 | | file://:0:0:0:0 | Load | m1834_6 | | file://:0:0:0:0 | Load | m1839_6 | +| file://:0:0:0:0 | Load | m2013_6 | | file://:0:0:0:0 | Load | ~m0_4 | | file://:0:0:0:0 | Load | ~m1444_6 | | file://:0:0:0:0 | Load | ~m1712_10 | @@ -832,6 +845,8 @@ | file://:0:0:0:0 | SideEffect | m0_4 | | file://:0:0:0:0 | SideEffect | m0_4 | | file://:0:0:0:0 | SideEffect | m0_4 | +| file://:0:0:0:0 | SideEffect | m0_4 | +| file://:0:0:0:0 | SideEffect | m0_4 | | file://:0:0:0:0 | SideEffect | m0_14 | | file://:0:0:0:0 | SideEffect | m1078_23 | | file://:0:0:0:0 | SideEffect | m1078_23 | @@ -869,6 +884,7 @@ | file://:0:0:0:0 | StoreValue | r0_7 | | file://:0:0:0:0 | StoreValue | r0_9 | | file://:0:0:0:0 | StoreValue | r0_9 | +| file://:0:0:0:0 | StoreValue | r0_9 | | file://:0:0:0:0 | StoreValue | r0_12 | | file://:0:0:0:0 | StoreValue | r0_12 | | file://:0:0:0:0 | StoreValue | r0_13 | @@ -895,6 +911,8 @@ | file://:0:0:0:0 | Unary | r0_7 | | file://:0:0:0:0 | Unary | r0_7 | | file://:0:0:0:0 | Unary | r0_7 | +| file://:0:0:0:0 | Unary | r0_7 | +| file://:0:0:0:0 | Unary | r0_8 | | file://:0:0:0:0 | Unary | r0_8 | | file://:0:0:0:0 | Unary | r0_9 | | file://:0:0:0:0 | Unary | r0_9 | @@ -914,6 +932,52 @@ | file://:0:0:0:0 | Unary | r0_20 | | file://:0:0:0:0 | Unary | r0_21 | | file://:0:0:0:0 | Unary | r0_21 | +| ir.c:7:6:7:17 | ChiPartial | partial:m7_3 | +| ir.c:7:6:7:17 | ChiTotal | total:m7_2 | +| ir.c:7:6:7:17 | SideEffect | ~m10_6 | +| ir.c:7:23:7:25 | Address | &:r7_5 | +| ir.c:8:12:8:17 | Address | &:r8_1 | +| ir.c:8:12:8:17 | Unary | r8_1 | +| ir.c:8:12:8:17 | Unary | r8_1 | +| ir.c:8:20:8:23 | Address | &:r8_3 | +| ir.c:8:20:8:23 | Address | &:r8_7 | +| ir.c:8:20:8:23 | ChiPartial | partial:m8_9 | +| ir.c:8:20:8:23 | ChiTotal | total:m8_6 | +| ir.c:8:20:8:23 | StoreValue | r8_8 | +| ir.c:8:22:8:22 | ChiPartial | partial:m8_5 | +| ir.c:8:22:8:22 | ChiTotal | total:m8_2 | +| ir.c:8:22:8:22 | StoreValue | r8_4 | +| ir.c:9:3:9:8 | Unary | r9_10 | +| ir.c:9:3:9:31 | ChiPartial | partial:m9_12 | +| ir.c:9:3:9:31 | ChiTotal | total:m9_8 | +| ir.c:9:10:9:10 | Address | &:r9_11 | +| ir.c:9:14:9:19 | Unary | r9_5 | +| ir.c:9:14:9:31 | ChiPartial | partial:m9_7 | +| ir.c:9:14:9:31 | ChiTotal | total:m8_10 | +| ir.c:9:14:9:31 | StoreValue | r9_9 | +| ir.c:9:21:9:21 | Address | &:r9_6 | +| ir.c:9:25:9:27 | Address | &:r9_1 | +| ir.c:9:25:9:27 | Left | r9_2 | +| ir.c:9:25:9:27 | Load | m7_6 | +| ir.c:9:25:9:31 | StoreValue | r9_4 | +| ir.c:9:25:9:31 | Unary | r9_4 | +| ir.c:9:31:9:31 | Right | r9_3 | +| ir.c:10:3:10:8 | Unary | r10_10 | +| ir.c:10:3:10:26 | ChiPartial | partial:m10_12 | +| ir.c:10:3:10:26 | ChiTotal | total:m10_9 | +| ir.c:10:10:10:10 | Address | &:r10_11 | +| ir.c:10:14:10:17 | CallTarget | func:r10_1 | +| ir.c:10:14:10:17 | ChiPartial | partial:m10_5 | +| ir.c:10:14:10:17 | ChiTotal | total:m7_4 | +| ir.c:10:14:10:17 | SideEffect | ~m7_4 | +| ir.c:10:14:10:17 | StoreValue | r10_4 | +| ir.c:10:19:10:25 | Address | &:r10_3 | +| ir.c:10:19:10:25 | Address | &:r10_3 | +| ir.c:10:19:10:25 | Arg(0) | 0:r10_3 | +| ir.c:10:19:10:25 | ChiPartial | partial:m10_8 | +| ir.c:10:19:10:25 | ChiTotal | total:m9_13 | +| ir.c:10:19:10:25 | SideEffect | ~m9_13 | +| ir.c:10:20:10:25 | Unary | r10_2 | | ir.cpp:1:6:1:14 | ChiPartial | partial:m1_3 | | ir.cpp:1:6:1:14 | ChiTotal | total:m1_2 | | ir.cpp:1:6:1:14 | SideEffect | m1_3 | @@ -9058,6 +9122,446 @@ | ir.cpp:1992:23:1992:45 | StoreValue | r1992_2 | | ir.cpp:1993:5:1993:7 | Address | &:r1993_3 | | ir.cpp:1993:13:1993:32 | StoreValue | r1993_2 | +| ir.cpp:1996:6:1996:19 | ChiPartial | partial:m1996_3 | +| ir.cpp:1996:6:1996:19 | ChiTotal | total:m1996_2 | +| ir.cpp:1996:6:1996:19 | SideEffect | ~m2000_9 | +| ir.cpp:1996:26:1996:26 | Address | &:r1996_5 | +| ir.cpp:1996:33:1996:33 | Address | &:r1996_7 | +| ir.cpp:1996:40:1996:40 | Address | &:r1996_9 | +| ir.cpp:1996:47:1996:47 | Address | &:r1996_11 | +| ir.cpp:1997:5:1997:5 | Address | &:r1997_7 | +| ir.cpp:1997:9:1997:9 | Address | &:r1997_1 | +| ir.cpp:1997:9:1997:9 | Condition | r1997_2 | +| ir.cpp:1997:9:1997:9 | Load | m1996_6 | +| ir.cpp:1997:9:1997:17 | Address | &:r1997_5 | +| ir.cpp:1997:9:1997:17 | Address | &:r1997_11 | +| ir.cpp:1997:9:1997:17 | Address | &:r1997_15 | +| ir.cpp:1997:9:1997:17 | Load | m1997_4 | +| ir.cpp:1997:9:1997:17 | Phi | from 2:m1997_12 | +| ir.cpp:1997:9:1997:17 | Phi | from 3:m1997_16 | +| ir.cpp:1997:9:1997:17 | StoreValue | r1997_6 | +| ir.cpp:1997:13:1997:13 | Address | &:r1997_9 | +| ir.cpp:1997:13:1997:13 | Load | m1996_8 | +| ir.cpp:1997:13:1997:13 | StoreValue | r1997_10 | +| ir.cpp:1997:17:1997:17 | Address | &:r1997_13 | +| ir.cpp:1997:17:1997:17 | Load | m1996_10 | +| ir.cpp:1997:17:1997:17 | StoreValue | r1997_14 | +| ir.cpp:1998:5:1998:5 | Address | &:r1998_7 | +| ir.cpp:1998:9:1998:9 | Address | &:r1998_1 | +| ir.cpp:1998:9:1998:9 | Condition | r1998_2 | +| ir.cpp:1998:9:1998:9 | Load | m1996_6 | +| ir.cpp:1998:9:1998:17 | Address | &:r1998_5 | +| ir.cpp:1998:9:1998:17 | Address | &:r1998_11 | +| ir.cpp:1998:9:1998:17 | Address | &:r1998_14 | +| ir.cpp:1998:9:1998:17 | Load | m1998_4 | +| ir.cpp:1998:9:1998:17 | Phi | from 5:m1998_12 | +| ir.cpp:1998:9:1998:17 | Phi | from 6:m1998_15 | +| ir.cpp:1998:9:1998:17 | StoreValue | r1998_6 | +| ir.cpp:1998:13:1998:13 | Address | &:r1998_9 | +| ir.cpp:1998:13:1998:13 | Load | m1996_8 | +| ir.cpp:1998:13:1998:13 | StoreValue | r1998_10 | +| ir.cpp:1998:17:1998:17 | StoreValue | r1998_13 | +| ir.cpp:1999:5:1999:5 | Address | &:r1999_7 | +| ir.cpp:1999:9:1999:9 | Address | &:r1999_1 | +| ir.cpp:1999:9:1999:9 | Condition | r1999_2 | +| ir.cpp:1999:9:1999:9 | Load | m1996_6 | +| ir.cpp:1999:9:1999:17 | Address | &:r1999_5 | +| ir.cpp:1999:9:1999:17 | Address | &:r1999_10 | +| ir.cpp:1999:9:1999:17 | Address | &:r1999_13 | +| ir.cpp:1999:9:1999:17 | Load | m1999_4 | +| ir.cpp:1999:9:1999:17 | Phi | from 8:m1999_11 | +| ir.cpp:1999:9:1999:17 | Phi | from 9:m1999_14 | +| ir.cpp:1999:9:1999:17 | StoreValue | r1999_6 | +| ir.cpp:1999:13:1999:13 | StoreValue | r1999_9 | +| ir.cpp:1999:17:1999:17 | StoreValue | r1999_12 | +| ir.cpp:2000:5:2000:19 | ChiPartial | partial:m2000_8 | +| ir.cpp:2000:5:2000:19 | ChiTotal | total:m1996_4 | +| ir.cpp:2000:6:2000:6 | Address | &:r2000_2 | +| ir.cpp:2000:6:2000:6 | Condition | r2000_3 | +| ir.cpp:2000:6:2000:6 | Load | m1996_6 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_6 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_7 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_11 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_14 | +| ir.cpp:2000:6:2000:14 | Load | m2000_5 | +| ir.cpp:2000:6:2000:14 | Phi | from 11:m2000_12 | +| ir.cpp:2000:6:2000:14 | Phi | from 12:m2000_15 | +| ir.cpp:2000:10:2000:10 | StoreValue | r2000_10 | +| ir.cpp:2000:14:2000:14 | StoreValue | r2000_13 | +| ir.cpp:2000:19:2000:19 | StoreValue | r2000_1 | +| ir.cpp:2006:6:2006:22 | ChiPartial | partial:m2006_3 | +| ir.cpp:2006:6:2006:22 | ChiTotal | total:m2006_2 | +| ir.cpp:2006:6:2006:22 | SideEffect | m2006_3 | +| ir.cpp:2006:29:2006:29 | Address | &:r2006_5 | +| ir.cpp:2006:46:2006:46 | Address | &:r2006_7 | +| ir.cpp:2006:63:2006:63 | Address | &:r2006_9 | +| ir.cpp:2006:80:2006:80 | Address | &:r2006_11 | +| ir.cpp:2007:5:2007:5 | Address | &:r2007_7 | +| ir.cpp:2007:9:2007:9 | Address | &:r2007_1 | +| ir.cpp:2007:9:2007:9 | Condition | r2007_2 | +| ir.cpp:2007:9:2007:9 | Load | m2006_6 | +| ir.cpp:2007:9:2007:17 | Address | &:r2007_5 | +| ir.cpp:2007:9:2007:17 | Address | &:r2007_11 | +| ir.cpp:2007:9:2007:17 | Address | &:r2007_15 | +| ir.cpp:2007:9:2007:17 | Load | m2007_4 | +| ir.cpp:2007:9:2007:17 | Phi | from 2:m2007_12 | +| ir.cpp:2007:9:2007:17 | Phi | from 3:m2007_16 | +| ir.cpp:2007:9:2007:17 | StoreValue | r2007_6 | +| ir.cpp:2007:13:2007:13 | Address | &:r2007_9 | +| ir.cpp:2007:13:2007:13 | Load | m2006_8 | +| ir.cpp:2007:13:2007:13 | StoreValue | r2007_10 | +| ir.cpp:2007:17:2007:17 | Address | &:r2007_13 | +| ir.cpp:2007:17:2007:17 | Load | m2006_10 | +| ir.cpp:2007:17:2007:17 | StoreValue | r2007_14 | +| ir.cpp:2008:5:2008:5 | Address | &:r2008_10 | +| ir.cpp:2008:9:2008:9 | Address | &:r2008_2 | +| ir.cpp:2008:9:2008:9 | Address | &:r2008_6 | +| ir.cpp:2008:9:2008:9 | Address | &:r2008_17 | +| ir.cpp:2008:9:2008:9 | Address | &:r2008_23 | +| ir.cpp:2008:9:2008:9 | Condition | r2008_3 | +| ir.cpp:2008:9:2008:9 | Load | m2006_6 | +| ir.cpp:2008:9:2008:9 | Load | m2008_5 | +| ir.cpp:2008:9:2008:9 | Phi | from 5:m2008_18 | +| ir.cpp:2008:9:2008:9 | Phi | from 6:m2008_24 | +| ir.cpp:2008:9:2008:9 | StoreValue | r2008_7 | +| ir.cpp:2008:9:2008:31 | Address | &:r2008_1 | +| ir.cpp:2008:9:2008:31 | Address | &:r2008_1 | +| ir.cpp:2008:9:2008:31 | Load | m2008_8 | +| ir.cpp:2008:9:2008:31 | StoreValue | r2008_9 | +| ir.cpp:2008:13:2008:13 | Address | &:r2008_12 | +| ir.cpp:2008:13:2008:13 | Address | &:r2008_12 | +| ir.cpp:2008:13:2008:13 | Address | &:r2008_13 | +| ir.cpp:2008:13:2008:13 | Load | m2006_8 | +| ir.cpp:2008:13:2008:13 | Load | m2008_15 | +| ir.cpp:2008:13:2008:13 | StoreValue | r2008_14 | +| ir.cpp:2008:13:2008:13 | StoreValue | r2008_16 | +| ir.cpp:2008:17:2008:31 | Address | &:r2008_19 | +| ir.cpp:2008:17:2008:31 | Address | &:r2008_19 | +| ir.cpp:2008:17:2008:31 | Load | m2008_21 | +| ir.cpp:2008:17:2008:31 | StoreValue | r2008_20 | +| ir.cpp:2008:17:2008:31 | StoreValue | r2008_22 | +| ir.cpp:2009:5:2009:5 | Address | &:r2009_10 | +| ir.cpp:2009:9:2009:9 | Address | &:r2009_2 | +| ir.cpp:2009:9:2009:9 | Address | &:r2009_6 | +| ir.cpp:2009:9:2009:9 | Address | &:r2009_16 | +| ir.cpp:2009:9:2009:9 | Address | &:r2009_22 | +| ir.cpp:2009:9:2009:9 | Condition | r2009_3 | +| ir.cpp:2009:9:2009:9 | Load | m2006_6 | +| ir.cpp:2009:9:2009:9 | Load | m2009_5 | +| ir.cpp:2009:9:2009:9 | Phi | from 8:m2009_17 | +| ir.cpp:2009:9:2009:9 | Phi | from 9:m2009_23 | +| ir.cpp:2009:9:2009:9 | StoreValue | r2009_7 | +| ir.cpp:2009:9:2009:45 | Address | &:r2009_1 | +| ir.cpp:2009:9:2009:45 | Address | &:r2009_1 | +| ir.cpp:2009:9:2009:45 | Load | m2009_8 | +| ir.cpp:2009:9:2009:45 | StoreValue | r2009_9 | +| ir.cpp:2009:13:2009:27 | Address | &:r2009_12 | +| ir.cpp:2009:13:2009:27 | Address | &:r2009_12 | +| ir.cpp:2009:13:2009:27 | Load | m2009_14 | +| ir.cpp:2009:13:2009:27 | StoreValue | r2009_13 | +| ir.cpp:2009:13:2009:27 | StoreValue | r2009_15 | +| ir.cpp:2009:31:2009:45 | Address | &:r2009_18 | +| ir.cpp:2009:31:2009:45 | Address | &:r2009_18 | +| ir.cpp:2009:31:2009:45 | Load | m2009_20 | +| ir.cpp:2009:31:2009:45 | StoreValue | r2009_19 | +| ir.cpp:2009:31:2009:45 | StoreValue | r2009_21 | +| ir.cpp:2010:6:2010:6 | Address | &:r2010_11 | +| ir.cpp:2010:6:2010:6 | Unary | r2010_11 | +| ir.cpp:2010:6:2010:18 | Address | &:r2010_13 | +| ir.cpp:2010:10:2010:10 | Address | &:r2010_5 | +| ir.cpp:2010:10:2010:10 | Condition | r2010_6 | +| ir.cpp:2010:10:2010:10 | Load | m2006_6 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_9 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_17 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_21 | +| ir.cpp:2010:10:2010:18 | Load | m2010_8 | +| ir.cpp:2010:10:2010:18 | Phi | from 11:m2010_18 | +| ir.cpp:2010:10:2010:18 | Phi | from 12:m2010_22 | +| ir.cpp:2010:10:2010:18 | StoreValue | r2010_10 | +| ir.cpp:2010:14:2010:14 | Address | &:r2010_15 | +| ir.cpp:2010:14:2010:14 | Load | m2006_8 | +| ir.cpp:2010:14:2010:14 | StoreValue | r2010_16 | +| ir.cpp:2010:18:2010:18 | Address | &:r2010_19 | +| ir.cpp:2010:18:2010:18 | Load | m2006_10 | +| ir.cpp:2010:18:2010:18 | StoreValue | r2010_20 | +| ir.cpp:2010:23:2010:37 | Address | &:r2010_1 | +| ir.cpp:2010:23:2010:37 | Address | &:r2010_1 | +| ir.cpp:2010:23:2010:37 | Load | m2010_3 | +| ir.cpp:2010:23:2010:37 | StoreValue | r2010_2 | +| ir.cpp:2010:23:2010:37 | StoreValue | r2010_4 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_7 | +| ir.cpp:2013:8:2013:8 | Address | &:r2013_10 | +| ir.cpp:2013:8:2013:8 | ChiPartial | partial:m2013_3 | +| ir.cpp:2013:8:2013:8 | ChiPartial | partial:m2013_3 | +| ir.cpp:2013:8:2013:8 | ChiPartial | partial:m2013_3 | +| ir.cpp:2013:8:2013:8 | ChiTotal | total:m2013_2 | +| ir.cpp:2013:8:2013:8 | ChiTotal | total:m2013_2 | +| ir.cpp:2013:8:2013:8 | ChiTotal | total:m2013_2 | +| ir.cpp:2013:8:2013:8 | Load | m0_10 | +| ir.cpp:2013:8:2013:8 | Load | m2013_6 | +| ir.cpp:2013:8:2013:8 | Load | m2013_6 | +| ir.cpp:2013:8:2013:8 | Load | m2013_6 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_3 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_3 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_3 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_8 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_8 | +| ir.cpp:2013:8:2013:8 | SideEffect | m2013_8 | +| ir.cpp:2014:13:2014:29 | Address | &:r2014_5 | +| ir.cpp:2014:13:2014:29 | Address | &:r2014_5 | +| ir.cpp:2014:13:2014:29 | Address | &:r2014_7 | +| ir.cpp:2014:13:2014:29 | Address | &:r2014_7 | +| ir.cpp:2014:13:2014:29 | ChiPartial | partial:m2014_3 | +| ir.cpp:2014:13:2014:29 | ChiTotal | total:m2014_2 | +| ir.cpp:2014:13:2014:29 | Load | m2014_6 | +| ir.cpp:2014:13:2014:29 | SideEffect | m2014_3 | +| ir.cpp:2014:13:2014:29 | SideEffect | m2014_8 | +| ir.cpp:2017:6:2017:25 | ChiPartial | partial:m2017_3 | +| ir.cpp:2017:6:2017:25 | ChiTotal | total:m2017_2 | +| ir.cpp:2017:6:2017:25 | SideEffect | ~m2021_32 | +| ir.cpp:2017:32:2017:32 | Address | &:r2017_5 | +| ir.cpp:2017:52:2017:52 | Address | &:r2017_7 | +| ir.cpp:2017:72:2017:72 | Address | &:r2017_9 | +| ir.cpp:2017:92:2017:92 | Address | &:r2017_11 | +| ir.cpp:2018:5:2018:5 | Address | &:r2018_1 | +| ir.cpp:2018:5:2018:5 | Address | &:r2018_1 | +| ir.cpp:2018:5:2018:5 | Arg(this) | this:r2018_1 | +| ir.cpp:2018:5:2018:5 | ChiPartial | partial:m2018_16 | +| ir.cpp:2018:5:2018:5 | ChiTotal | total:m2017_12 | +| ir.cpp:2018:5:2018:5 | SideEffect | m2017_12 | +| ir.cpp:2018:7:2018:7 | CallTarget | func:r2018_2 | +| ir.cpp:2018:7:2018:7 | ChiPartial | partial:m2018_12 | +| ir.cpp:2018:7:2018:7 | ChiTotal | total:m2017_4 | +| ir.cpp:2018:7:2018:7 | SideEffect | ~m2017_4 | +| ir.cpp:2018:7:2018:7 | Unary | r2018_11 | +| ir.cpp:2018:9:2018:9 | Address | &:r2018_3 | +| ir.cpp:2018:9:2018:9 | Condition | r2018_4 | +| ir.cpp:2018:9:2018:9 | Load | m2017_6 | +| ir.cpp:2018:9:2018:17 | Address | &:r2018_7 | +| ir.cpp:2018:9:2018:17 | Address | &:r2018_10 | +| ir.cpp:2018:9:2018:17 | Address | &:r2018_20 | +| ir.cpp:2018:9:2018:17 | Address | &:r2018_23 | +| ir.cpp:2018:9:2018:17 | Arg(0) | 0:r2018_10 | +| ir.cpp:2018:9:2018:17 | Load | m2018_6 | +| ir.cpp:2018:9:2018:17 | Phi | from 2:m2018_21 | +| ir.cpp:2018:9:2018:17 | Phi | from 3:m2018_24 | +| ir.cpp:2018:9:2018:17 | SideEffect | ~m2018_13 | +| ir.cpp:2018:9:2018:17 | Unary | r2018_8 | +| ir.cpp:2018:9:2018:17 | Unary | r2018_9 | +| ir.cpp:2018:13:2018:13 | StoreValue | r2018_19 | +| ir.cpp:2018:17:2018:17 | StoreValue | r2018_22 | +| ir.cpp:2019:5:2019:5 | Address | &:r2019_1 | +| ir.cpp:2019:5:2019:5 | Address | &:r2019_1 | +| ir.cpp:2019:5:2019:5 | Arg(this) | this:r2019_1 | +| ir.cpp:2019:5:2019:5 | ChiPartial | partial:m2019_19 | +| ir.cpp:2019:5:2019:5 | ChiTotal | total:m2018_17 | +| ir.cpp:2019:5:2019:5 | SideEffect | m2018_17 | +| ir.cpp:2019:7:2019:7 | CallTarget | func:r2019_2 | +| ir.cpp:2019:7:2019:7 | ChiPartial | partial:m2019_15 | +| ir.cpp:2019:7:2019:7 | ChiTotal | total:m2019_7 | +| ir.cpp:2019:7:2019:7 | SideEffect | ~m2019_7 | +| ir.cpp:2019:7:2019:7 | Unary | r2019_14 | +| ir.cpp:2019:9:2019:9 | Address | &:r2019_4 | +| ir.cpp:2019:9:2019:9 | Address | &:r2019_9 | +| ir.cpp:2019:9:2019:9 | Address | &:r2019_35 | +| ir.cpp:2019:9:2019:9 | Address | &:r2019_46 | +| ir.cpp:2019:9:2019:9 | Condition | r2019_5 | +| ir.cpp:2019:9:2019:9 | Load | m2017_6 | +| ir.cpp:2019:9:2019:9 | Load | m2019_8 | +| ir.cpp:2019:9:2019:9 | Phi | from 5:m2019_36 | +| ir.cpp:2019:9:2019:9 | Phi | from 5:~m2019_30 | +| ir.cpp:2019:9:2019:9 | Phi | from 6:m2019_47 | +| ir.cpp:2019:9:2019:9 | Phi | from 6:~m2019_42 | +| ir.cpp:2019:9:2019:9 | StoreValue | r2019_10 | +| ir.cpp:2019:9:2019:34 | Address | &:r2019_3 | +| ir.cpp:2019:9:2019:34 | Address | &:r2019_13 | +| ir.cpp:2019:9:2019:34 | Arg(0) | 0:r2019_13 | +| ir.cpp:2019:9:2019:34 | SideEffect | ~m2019_11 | +| ir.cpp:2019:9:2019:34 | Unary | r2019_3 | +| ir.cpp:2019:9:2019:34 | Unary | r2019_12 | +| ir.cpp:2019:13:2019:13 | Address | &:r2019_22 | +| ir.cpp:2019:13:2019:13 | Address | &:r2019_22 | +| ir.cpp:2019:13:2019:13 | Address | &:r2019_22 | +| ir.cpp:2019:13:2019:13 | Address | &:r2019_27 | +| ir.cpp:2019:13:2019:13 | Arg(0) | 0:r2019_27 | +| ir.cpp:2019:13:2019:13 | Arg(this) | this:r2019_22 | +| ir.cpp:2019:13:2019:13 | CallTarget | func:r2019_24 | +| ir.cpp:2019:13:2019:13 | ChiPartial | partial:m2019_29 | +| ir.cpp:2019:13:2019:13 | ChiPartial | partial:m2019_32 | +| ir.cpp:2019:13:2019:13 | ChiTotal | total:m2018_13 | +| ir.cpp:2019:13:2019:13 | ChiTotal | total:m2019_23 | +| ir.cpp:2019:13:2019:13 | Load | m2019_33 | +| ir.cpp:2019:13:2019:13 | SideEffect | ~m2017_8 | +| ir.cpp:2019:13:2019:13 | SideEffect | ~m2018_13 | +| ir.cpp:2019:13:2019:13 | StoreValue | r2019_34 | +| ir.cpp:2019:13:2019:13 | Unary | r2019_25 | +| ir.cpp:2019:13:2019:13 | Unary | r2019_26 | +| ir.cpp:2019:17:2019:34 | Address | &:r2019_37 | +| ir.cpp:2019:17:2019:34 | Address | &:r2019_37 | +| ir.cpp:2019:17:2019:34 | Address | &:r2019_37 | +| ir.cpp:2019:17:2019:34 | Arg(this) | this:r2019_37 | +| ir.cpp:2019:17:2019:34 | CallTarget | func:r2019_39 | +| ir.cpp:2019:17:2019:34 | ChiPartial | partial:m2019_41 | +| ir.cpp:2019:17:2019:34 | ChiPartial | partial:m2019_43 | +| ir.cpp:2019:17:2019:34 | ChiTotal | total:m2018_13 | +| ir.cpp:2019:17:2019:34 | ChiTotal | total:m2019_38 | +| ir.cpp:2019:17:2019:34 | Load | m2019_44 | +| ir.cpp:2019:17:2019:34 | SideEffect | ~m2018_13 | +| ir.cpp:2019:17:2019:34 | StoreValue | r2019_45 | +| ir.cpp:2020:5:2020:5 | Address | &:r2020_1 | +| ir.cpp:2020:5:2020:5 | Address | &:r2020_1 | +| ir.cpp:2020:5:2020:5 | Arg(this) | this:r2020_1 | +| ir.cpp:2020:5:2020:5 | ChiPartial | partial:m2020_19 | +| ir.cpp:2020:5:2020:5 | ChiTotal | total:m2019_20 | +| ir.cpp:2020:5:2020:5 | SideEffect | m2019_20 | +| ir.cpp:2020:7:2020:7 | CallTarget | func:r2020_2 | +| ir.cpp:2020:7:2020:7 | ChiPartial | partial:m2020_15 | +| ir.cpp:2020:7:2020:7 | ChiTotal | total:m2020_7 | +| ir.cpp:2020:7:2020:7 | SideEffect | ~m2020_7 | +| ir.cpp:2020:7:2020:7 | Unary | r2020_14 | +| ir.cpp:2020:9:2020:9 | Address | &:r2020_4 | +| ir.cpp:2020:9:2020:9 | Address | &:r2020_9 | +| ir.cpp:2020:9:2020:9 | Address | &:r2020_31 | +| ir.cpp:2020:9:2020:9 | Address | &:r2020_42 | +| ir.cpp:2020:9:2020:9 | Condition | r2020_5 | +| ir.cpp:2020:9:2020:9 | Load | m2017_6 | +| ir.cpp:2020:9:2020:9 | Load | m2020_8 | +| ir.cpp:2020:9:2020:9 | Phi | from 8:m2020_32 | +| ir.cpp:2020:9:2020:9 | Phi | from 8:~m2020_27 | +| ir.cpp:2020:9:2020:9 | Phi | from 9:m2020_43 | +| ir.cpp:2020:9:2020:9 | Phi | from 9:~m2020_38 | +| ir.cpp:2020:9:2020:9 | StoreValue | r2020_10 | +| ir.cpp:2020:9:2020:51 | Address | &:r2020_3 | +| ir.cpp:2020:9:2020:51 | Address | &:r2020_13 | +| ir.cpp:2020:9:2020:51 | Arg(0) | 0:r2020_13 | +| ir.cpp:2020:9:2020:51 | SideEffect | ~m2020_11 | +| ir.cpp:2020:9:2020:51 | Unary | r2020_3 | +| ir.cpp:2020:9:2020:51 | Unary | r2020_12 | +| ir.cpp:2020:13:2020:30 | Address | &:r2020_22 | +| ir.cpp:2020:13:2020:30 | Address | &:r2020_22 | +| ir.cpp:2020:13:2020:30 | Address | &:r2020_22 | +| ir.cpp:2020:13:2020:30 | Arg(this) | this:r2020_22 | +| ir.cpp:2020:13:2020:30 | CallTarget | func:r2020_24 | +| ir.cpp:2020:13:2020:30 | ChiPartial | partial:m2020_26 | +| ir.cpp:2020:13:2020:30 | ChiPartial | partial:m2020_28 | +| ir.cpp:2020:13:2020:30 | ChiTotal | total:m2019_16 | +| ir.cpp:2020:13:2020:30 | ChiTotal | total:m2020_23 | +| ir.cpp:2020:13:2020:30 | Load | m2020_29 | +| ir.cpp:2020:13:2020:30 | SideEffect | ~m2019_16 | +| ir.cpp:2020:13:2020:30 | StoreValue | r2020_30 | +| ir.cpp:2020:34:2020:51 | Address | &:r2020_33 | +| ir.cpp:2020:34:2020:51 | Address | &:r2020_33 | +| ir.cpp:2020:34:2020:51 | Address | &:r2020_33 | +| ir.cpp:2020:34:2020:51 | Arg(this) | this:r2020_33 | +| ir.cpp:2020:34:2020:51 | CallTarget | func:r2020_35 | +| ir.cpp:2020:34:2020:51 | ChiPartial | partial:m2020_37 | +| ir.cpp:2020:34:2020:51 | ChiPartial | partial:m2020_39 | +| ir.cpp:2020:34:2020:51 | ChiTotal | total:m2019_16 | +| ir.cpp:2020:34:2020:51 | ChiTotal | total:m2020_34 | +| ir.cpp:2020:34:2020:51 | Load | m2020_40 | +| ir.cpp:2020:34:2020:51 | SideEffect | ~m2019_16 | +| ir.cpp:2020:34:2020:51 | StoreValue | r2020_41 | +| ir.cpp:2021:5:2021:19 | ChiPartial | partial:m2021_35 | +| ir.cpp:2021:5:2021:19 | ChiTotal | total:m2021_17 | +| ir.cpp:2021:5:2021:19 | SideEffect | m2021_17 | +| ir.cpp:2021:6:2021:6 | Address | &:r2021_1 | +| ir.cpp:2021:6:2021:6 | Address | &:r2021_1 | +| ir.cpp:2021:6:2021:6 | Arg(this) | this:r2021_1 | +| ir.cpp:2021:6:2021:6 | ChiPartial | partial:m2021_16 | +| ir.cpp:2021:6:2021:6 | ChiTotal | total:m2020_20 | +| ir.cpp:2021:6:2021:6 | SideEffect | m2020_20 | +| ir.cpp:2021:8:2021:8 | CallTarget | func:r2021_2 | +| ir.cpp:2021:8:2021:8 | ChiPartial | partial:m2021_12 | +| ir.cpp:2021:8:2021:8 | ChiTotal | total:m2020_16 | +| ir.cpp:2021:8:2021:8 | SideEffect | ~m2020_16 | +| ir.cpp:2021:8:2021:8 | Unary | r2021_11 | +| ir.cpp:2021:8:2021:19 | Address | &:r2021_18 | +| ir.cpp:2021:8:2021:19 | Address | &:r2021_18 | +| ir.cpp:2021:8:2021:19 | Arg(this) | this:r2021_18 | +| ir.cpp:2021:10:2021:10 | Address | &:r2021_3 | +| ir.cpp:2021:10:2021:10 | Condition | r2021_4 | +| ir.cpp:2021:10:2021:10 | Load | m2017_6 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_7 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_10 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_39 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_42 | +| ir.cpp:2021:10:2021:18 | Arg(0) | 0:r2021_10 | +| ir.cpp:2021:10:2021:18 | Load | m2021_6 | +| ir.cpp:2021:10:2021:18 | Phi | from 11:m2021_40 | +| ir.cpp:2021:10:2021:18 | Phi | from 12:m2021_43 | +| ir.cpp:2021:10:2021:18 | SideEffect | ~m2021_13 | +| ir.cpp:2021:10:2021:18 | Unary | r2021_8 | +| ir.cpp:2021:10:2021:18 | Unary | r2021_9 | +| ir.cpp:2021:14:2021:14 | StoreValue | r2021_38 | +| ir.cpp:2021:18:2021:18 | StoreValue | r2021_41 | +| ir.cpp:2021:21:2021:21 | CallTarget | func:r2021_19 | +| ir.cpp:2021:21:2021:21 | ChiPartial | partial:m2021_31 | +| ir.cpp:2021:21:2021:21 | ChiTotal | total:m2021_25 | +| ir.cpp:2021:21:2021:21 | SideEffect | ~m2021_25 | +| ir.cpp:2021:21:2021:21 | Unary | r2021_30 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_20 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_20 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_29 | +| ir.cpp:2021:23:2021:40 | Arg(0) | 0:r2021_29 | +| ir.cpp:2021:23:2021:40 | Arg(this) | this:r2021_20 | +| ir.cpp:2021:23:2021:40 | CallTarget | func:r2021_22 | +| ir.cpp:2021:23:2021:40 | ChiPartial | partial:m2021_24 | +| ir.cpp:2021:23:2021:40 | ChiPartial | partial:m2021_26 | +| ir.cpp:2021:23:2021:40 | ChiTotal | total:m2021_13 | +| ir.cpp:2021:23:2021:40 | ChiTotal | total:m2021_21 | +| ir.cpp:2021:23:2021:40 | SideEffect | ~m2021_13 | +| ir.cpp:2021:23:2021:40 | SideEffect | ~m2021_27 | +| ir.cpp:2021:23:2021:40 | Unary | r2021_20 | +| ir.cpp:2021:23:2021:40 | Unary | r2021_28 | +| ir.cpp:2026:14:2026:22 | ChiPartial | partial:m2026_3 | +| ir.cpp:2026:14:2026:22 | ChiTotal | total:m2026_2 | +| ir.cpp:2026:37:2026:37 | Address | &:r2026_5 | +| ir.cpp:2027:16:2027:16 | Address | &:r2027_1 | +| ir.cpp:2028:3:2028:3 | Address | &:r2028_9 | +| ir.cpp:2028:7:2028:7 | Address | &:r2028_1 | +| ir.cpp:2028:7:2028:7 | Left | r2028_2 | +| ir.cpp:2028:7:2028:7 | Load | m2026_6 | +| ir.cpp:2028:7:2028:13 | Condition | r2028_4 | +| ir.cpp:2028:7:2030:28 | Address | &:r2028_7 | +| ir.cpp:2028:7:2030:28 | Address | &:r2028_11 | +| ir.cpp:2028:7:2030:28 | Address | &:r2028_13 | +| ir.cpp:2028:7:2030:28 | Load | m2028_6 | +| ir.cpp:2028:7:2030:28 | Phi | from 2:m2028_12 | +| ir.cpp:2028:7:2030:28 | Phi | from 3:m2028_14 | +| ir.cpp:2028:7:2030:28 | StoreValue | r2028_8 | +| ir.cpp:2028:11:2028:13 | Right | r2028_3 | +| ir.cpp:2029:6:2029:20 | CallTarget | func:r2029_1 | +| ir.cpp:2029:6:2029:20 | ChiPartial | partial:m2029_5 | +| ir.cpp:2029:6:2029:20 | ChiTotal | total:m2026_4 | +| ir.cpp:2029:6:2029:20 | SideEffect | ~m2026_4 | +| ir.cpp:2029:6:2029:26 | StoreValue | r2029_9 | +| ir.cpp:2029:22:2029:22 | Address | &:r2029_2 | +| ir.cpp:2029:22:2029:22 | Arg(0) | 0:r2029_3 | +| ir.cpp:2029:22:2029:22 | Load | m2026_6 | +| ir.cpp:2029:26:2029:26 | Address | &:r2029_7 | +| ir.cpp:2029:26:2029:26 | Load | m2026_6 | +| ir.cpp:2029:26:2029:26 | Unary | r2029_8 | +| ir.cpp:2030:5:2030:28 | StoreValue | r2030_9 | +| ir.cpp:2030:6:2030:20 | CallTarget | func:r2030_1 | +| ir.cpp:2030:6:2030:20 | ChiPartial | partial:m2030_5 | +| ir.cpp:2030:6:2030:20 | ChiTotal | total:m2026_4 | +| ir.cpp:2030:6:2030:20 | SideEffect | ~m2026_4 | +| ir.cpp:2030:6:2030:27 | Unary | r2030_8 | +| ir.cpp:2030:22:2030:22 | Address | &:r2030_2 | +| ir.cpp:2030:22:2030:22 | Arg(0) | 0:r2030_3 | +| ir.cpp:2030:22:2030:22 | Load | m2026_6 | +| ir.cpp:2030:26:2030:27 | Unary | r2030_7 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index db549003927..79dd36ed029 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -724,6 +724,48 @@ complex.c: # 58| v58_5(void) = AliasedUse : ~m? # 58| v58_6(void) = ExitFunction : +ir.c: +# 7| void MyCoordsTest(int) +# 7| Block 0 +# 7| v7_1(void) = EnterFunction : +# 7| mu7_2(unknown) = AliasedDefinition : +# 7| mu7_3(unknown) = InitializeNonLocal : +# 7| r7_4(glval) = VariableAddress[pos] : +# 7| mu7_5(int) = InitializeParameter[pos] : &:r7_4 +# 8| r8_1(glval<(unnamed class/struct/union)>) = VariableAddress[coords] : +# 8| mu8_2((unnamed class/struct/union)) = Uninitialized[coords] : &:r8_1 +# 8| r8_3(glval) = FieldAddress[x] : r8_1 +# 8| r8_4(int) = Constant[0] : +# 8| mu8_5(int) = Store[?] : &:r8_3, r8_4 +# 8| r8_6(glval) = FieldAddress[y] : r8_1 +# 8| r8_7(int) = Constant[0] : +# 8| mu8_8(int) = Store[?] : &:r8_6, r8_7 +# 9| r9_1(glval) = VariableAddress[pos] : +# 9| r9_2(int) = Load[pos] : &:r9_1, ~m? +# 9| r9_3(int) = Constant[1] : +# 9| r9_4(int) = Add : r9_2, r9_3 +# 9| r9_5(glval<(unnamed class/struct/union)>) = VariableAddress[coords] : +# 9| r9_6(glval) = FieldAddress[y] : r9_5 +# 9| mu9_7(int) = Store[?] : &:r9_6, r9_4 +# 9| r9_8(int) = CopyValue : r9_4 +# 9| r9_9(glval<(unnamed class/struct/union)>) = VariableAddress[coords] : +# 9| r9_10(glval) = FieldAddress[x] : r9_9 +# 9| mu9_11(int) = Store[?] : &:r9_10, r9_8 +# 10| r10_1(glval) = FunctionAddress[getX] : +# 10| r10_2(glval<(unnamed class/struct/union)>) = VariableAddress[coords] : +# 10| r10_3(struct *) = CopyValue : r10_2 +# 10| r10_4(int) = Call[getX] : func:r10_1, 0:r10_3 +# 10| mu10_5(unknown) = ^CallSideEffect : ~m? +# 10| v10_6(void) = ^BufferReadSideEffect[0] : &:r10_3, ~m? +# 10| mu10_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r10_3 +# 10| r10_8(glval<(unnamed class/struct/union)>) = VariableAddress[coords] : +# 10| r10_9(glval) = FieldAddress[x] : r10_8 +# 10| mu10_10(int) = Store[?] : &:r10_9, r10_4 +# 11| v11_1(void) = NoOp : +# 7| v7_6(void) = ReturnVoid : +# 7| v7_7(void) = AliasedUse : ~m? +# 7| v7_8(void) = ExitFunction : + ir.cpp: # 1| void Constants() # 1| Block 0 @@ -10456,6 +10498,579 @@ ir.cpp: # 1990| v1990_5(void) = AliasedUse : ~m? # 1990| v1990_6(void) = ExitFunction : +# 1996| void TernaryTestInt(bool, int, int, int) +# 1996| Block 0 +# 1996| v1996_1(void) = EnterFunction : +# 1996| mu1996_2(unknown) = AliasedDefinition : +# 1996| mu1996_3(unknown) = InitializeNonLocal : +# 1996| r1996_4(glval) = VariableAddress[a] : +# 1996| mu1996_5(bool) = InitializeParameter[a] : &:r1996_4 +# 1996| r1996_6(glval) = VariableAddress[x] : +# 1996| mu1996_7(int) = InitializeParameter[x] : &:r1996_6 +# 1996| r1996_8(glval) = VariableAddress[y] : +# 1996| mu1996_9(int) = InitializeParameter[y] : &:r1996_8 +# 1996| r1996_10(glval) = VariableAddress[z] : +# 1996| mu1996_11(int) = InitializeParameter[z] : &:r1996_10 +# 1997| r1997_1(glval) = VariableAddress[a] : +# 1997| r1997_2(bool) = Load[a] : &:r1997_1, ~m? +# 1997| v1997_3(void) = ConditionalBranch : r1997_2 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 1997| Block 1 +# 1997| r1997_4(glval) = VariableAddress[#temp1997:9] : +# 1997| r1997_5(int) = Load[#temp1997:9] : &:r1997_4, ~m? +# 1997| r1997_6(glval) = VariableAddress[z] : +# 1997| mu1997_7(int) = Store[z] : &:r1997_6, r1997_5 +# 1998| r1998_1(glval) = VariableAddress[a] : +# 1998| r1998_2(bool) = Load[a] : &:r1998_1, ~m? +# 1998| v1998_3(void) = ConditionalBranch : r1998_2 +#-----| False -> Block 6 +#-----| True -> Block 5 + +# 1997| Block 2 +# 1997| r1997_8(glval) = VariableAddress[x] : +# 1997| r1997_9(int) = Load[x] : &:r1997_8, ~m? +# 1997| r1997_10(glval) = VariableAddress[#temp1997:9] : +# 1997| mu1997_11(int) = Store[#temp1997:9] : &:r1997_10, r1997_9 +#-----| Goto -> Block 1 + +# 1997| Block 3 +# 1997| r1997_12(glval) = VariableAddress[y] : +# 1997| r1997_13(int) = Load[y] : &:r1997_12, ~m? +# 1997| r1997_14(glval) = VariableAddress[#temp1997:9] : +# 1997| mu1997_15(int) = Store[#temp1997:9] : &:r1997_14, r1997_13 +#-----| Goto -> Block 1 + +# 1998| Block 4 +# 1998| r1998_4(glval) = VariableAddress[#temp1998:9] : +# 1998| r1998_5(int) = Load[#temp1998:9] : &:r1998_4, ~m? +# 1998| r1998_6(glval) = VariableAddress[z] : +# 1998| mu1998_7(int) = Store[z] : &:r1998_6, r1998_5 +# 1999| r1999_1(glval) = VariableAddress[a] : +# 1999| r1999_2(bool) = Load[a] : &:r1999_1, ~m? +# 1999| v1999_3(void) = ConditionalBranch : r1999_2 +#-----| False -> Block 9 +#-----| True -> Block 8 + +# 1998| Block 5 +# 1998| r1998_8(glval) = VariableAddress[x] : +# 1998| r1998_9(int) = Load[x] : &:r1998_8, ~m? +# 1998| r1998_10(glval) = VariableAddress[#temp1998:9] : +# 1998| mu1998_11(int) = Store[#temp1998:9] : &:r1998_10, r1998_9 +#-----| Goto -> Block 4 + +# 1998| Block 6 +# 1998| r1998_12(int) = Constant[5] : +# 1998| r1998_13(glval) = VariableAddress[#temp1998:9] : +# 1998| mu1998_14(int) = Store[#temp1998:9] : &:r1998_13, r1998_12 +#-----| Goto -> Block 4 + +# 1999| Block 7 +# 1999| r1999_4(glval) = VariableAddress[#temp1999:9] : +# 1999| r1999_5(int) = Load[#temp1999:9] : &:r1999_4, ~m? +# 1999| r1999_6(glval) = VariableAddress[z] : +# 1999| mu1999_7(int) = Store[z] : &:r1999_6, r1999_5 +# 2000| r2000_1(int) = Constant[7] : +# 2000| r2000_2(glval) = VariableAddress[a] : +# 2000| r2000_3(bool) = Load[a] : &:r2000_2, ~m? +# 2000| v2000_4(void) = ConditionalBranch : r2000_3 +#-----| False -> Block 12 +#-----| True -> Block 11 + +# 1999| Block 8 +# 1999| r1999_8(int) = Constant[3] : +# 1999| r1999_9(glval) = VariableAddress[#temp1999:9] : +# 1999| mu1999_10(int) = Store[#temp1999:9] : &:r1999_9, r1999_8 +#-----| Goto -> Block 7 + +# 1999| Block 9 +# 1999| r1999_11(int) = Constant[5] : +# 1999| r1999_12(glval) = VariableAddress[#temp1999:9] : +# 1999| mu1999_13(int) = Store[#temp1999:9] : &:r1999_12, r1999_11 +#-----| Goto -> Block 7 + +# 2000| Block 10 +# 2000| r2000_5(glval) = VariableAddress[#temp2000:6] : +# 2000| r2000_6(glval) = Load[#temp2000:6] : &:r2000_5, ~m? +# 2000| mu2000_7(int) = Store[?] : &:r2000_6, r2000_1 +# 2001| v2001_1(void) = NoOp : +# 1996| v1996_12(void) = ReturnVoid : +# 1996| v1996_13(void) = AliasedUse : ~m? +# 1996| v1996_14(void) = ExitFunction : + +# 2000| Block 11 +# 2000| r2000_8(glval) = VariableAddress[x] : +# 2000| r2000_9(glval) = VariableAddress[#temp2000:6] : +# 2000| mu2000_10(glval) = Store[#temp2000:6] : &:r2000_9, r2000_8 +#-----| Goto -> Block 10 + +# 2000| Block 12 +# 2000| r2000_11(glval) = VariableAddress[y] : +# 2000| r2000_12(glval) = VariableAddress[#temp2000:6] : +# 2000| mu2000_13(glval) = Store[#temp2000:6] : &:r2000_12, r2000_11 +#-----| Goto -> Block 10 + +# 2006| void TernaryTestPodObj(bool, TernaryPodObj, TernaryPodObj, TernaryPodObj) +# 2006| Block 0 +# 2006| v2006_1(void) = EnterFunction : +# 2006| mu2006_2(unknown) = AliasedDefinition : +# 2006| mu2006_3(unknown) = InitializeNonLocal : +# 2006| r2006_4(glval) = VariableAddress[a] : +# 2006| mu2006_5(bool) = InitializeParameter[a] : &:r2006_4 +# 2006| r2006_6(glval) = VariableAddress[x] : +# 2006| mu2006_7(TernaryPodObj) = InitializeParameter[x] : &:r2006_6 +# 2006| r2006_8(glval) = VariableAddress[y] : +# 2006| mu2006_9(TernaryPodObj) = InitializeParameter[y] : &:r2006_8 +# 2006| r2006_10(glval) = VariableAddress[z] : +# 2006| mu2006_11(TernaryPodObj) = InitializeParameter[z] : &:r2006_10 +# 2007| r2007_1(glval) = VariableAddress[a] : +# 2007| r2007_2(bool) = Load[a] : &:r2007_1, ~m? +# 2007| v2007_3(void) = ConditionalBranch : r2007_2 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 2007| Block 1 +# 2007| r2007_4(glval) = VariableAddress[#temp2007:9] : +# 2007| r2007_5(TernaryPodObj) = Load[#temp2007:9] : &:r2007_4, ~m? +# 2007| r2007_6(glval) = VariableAddress[z] : +# 2007| mu2007_7(TernaryPodObj) = Store[z] : &:r2007_6, r2007_5 +# 2008| r2008_1(glval) = VariableAddress[#temp2008:9] : +# 2008| r2008_2(glval) = VariableAddress[a] : +# 2008| r2008_3(bool) = Load[a] : &:r2008_2, ~m? +# 2008| v2008_4(void) = ConditionalBranch : r2008_3 +#-----| False -> Block 6 +#-----| True -> Block 5 + +# 2007| Block 2 +# 2007| r2007_8(glval) = VariableAddress[x] : +# 2007| r2007_9(TernaryPodObj) = Load[x] : &:r2007_8, ~m? +# 2007| r2007_10(glval) = VariableAddress[#temp2007:9] : +# 2007| mu2007_11(TernaryPodObj) = Store[#temp2007:9] : &:r2007_10, r2007_9 +#-----| Goto -> Block 1 + +# 2007| Block 3 +# 2007| r2007_12(glval) = VariableAddress[y] : +# 2007| r2007_13(TernaryPodObj) = Load[y] : &:r2007_12, ~m? +# 2007| r2007_14(glval) = VariableAddress[#temp2007:9] : +# 2007| mu2007_15(TernaryPodObj) = Store[#temp2007:9] : &:r2007_14, r2007_13 +#-----| Goto -> Block 1 + +# 2008| Block 4 +# 2008| r2008_5(glval) = VariableAddress[#temp2008:9] : +# 2008| r2008_6(TernaryPodObj) = Load[#temp2008:9] : &:r2008_5, ~m? +# 2008| mu2008_7(TernaryPodObj) = Store[#temp2008:9] : &:r2008_1, r2008_6 +# 2008| r2008_8(TernaryPodObj) = Load[#temp2008:9] : &:r2008_1, ~m? +# 2008| r2008_9(glval) = VariableAddress[z] : +# 2008| mu2008_10(TernaryPodObj) = Store[z] : &:r2008_9, r2008_8 +# 2009| r2009_1(glval) = VariableAddress[#temp2009:9] : +# 2009| r2009_2(glval) = VariableAddress[a] : +# 2009| r2009_3(bool) = Load[a] : &:r2009_2, ~m? +# 2009| v2009_4(void) = ConditionalBranch : r2009_3 +#-----| False -> Block 9 +#-----| True -> Block 8 + +# 2008| Block 5 +# 2008| r2008_11(glval) = VariableAddress[#temp2008:13] : +# 2008| r2008_12(glval) = VariableAddress[x] : +# 2008| r2008_13(TernaryPodObj) = Load[x] : &:r2008_12, ~m? +# 2008| mu2008_14(TernaryPodObj) = Store[#temp2008:13] : &:r2008_11, r2008_13 +# 2008| r2008_15(TernaryPodObj) = Load[#temp2008:13] : &:r2008_11, ~m? +# 2008| r2008_16(glval) = VariableAddress[#temp2008:9] : +# 2008| mu2008_17(TernaryPodObj) = Store[#temp2008:9] : &:r2008_16, r2008_15 +#-----| Goto -> Block 4 + +# 2008| Block 6 +# 2008| r2008_18(glval) = VariableAddress[#temp2008:17] : +# 2008| r2008_19(TernaryPodObj) = Constant[0] : +# 2008| mu2008_20(TernaryPodObj) = Store[#temp2008:17] : &:r2008_18, r2008_19 +# 2008| r2008_21(TernaryPodObj) = Load[#temp2008:17] : &:r2008_18, ~m? +# 2008| r2008_22(glval) = VariableAddress[#temp2008:9] : +# 2008| mu2008_23(TernaryPodObj) = Store[#temp2008:9] : &:r2008_22, r2008_21 +#-----| Goto -> Block 4 + +# 2009| Block 7 +# 2009| r2009_5(glval) = VariableAddress[#temp2009:9] : +# 2009| r2009_6(TernaryPodObj) = Load[#temp2009:9] : &:r2009_5, ~m? +# 2009| mu2009_7(TernaryPodObj) = Store[#temp2009:9] : &:r2009_1, r2009_6 +# 2009| r2009_8(TernaryPodObj) = Load[#temp2009:9] : &:r2009_1, ~m? +# 2009| r2009_9(glval) = VariableAddress[z] : +# 2009| mu2009_10(TernaryPodObj) = Store[z] : &:r2009_9, r2009_8 +# 2010| r2010_1(glval) = VariableAddress[#temp2010:23] : +# 2010| r2010_2(TernaryPodObj) = Constant[0] : +# 2010| mu2010_3(TernaryPodObj) = Store[#temp2010:23] : &:r2010_1, r2010_2 +# 2010| r2010_4(TernaryPodObj) = Load[#temp2010:23] : &:r2010_1, ~m? +# 2010| r2010_5(glval) = VariableAddress[a] : +# 2010| r2010_6(bool) = Load[a] : &:r2010_5, ~m? +# 2010| v2010_7(void) = ConditionalBranch : r2010_6 +#-----| False -> Block 12 +#-----| True -> Block 11 + +# 2009| Block 8 +# 2009| r2009_11(glval) = VariableAddress[#temp2009:13] : +# 2009| r2009_12(TernaryPodObj) = Constant[0] : +# 2009| mu2009_13(TernaryPodObj) = Store[#temp2009:13] : &:r2009_11, r2009_12 +# 2009| r2009_14(TernaryPodObj) = Load[#temp2009:13] : &:r2009_11, ~m? +# 2009| r2009_15(glval) = VariableAddress[#temp2009:9] : +# 2009| mu2009_16(TernaryPodObj) = Store[#temp2009:9] : &:r2009_15, r2009_14 +#-----| Goto -> Block 7 + +# 2009| Block 9 +# 2009| r2009_17(glval) = VariableAddress[#temp2009:31] : +# 2009| r2009_18(TernaryPodObj) = Constant[0] : +# 2009| mu2009_19(TernaryPodObj) = Store[#temp2009:31] : &:r2009_17, r2009_18 +# 2009| r2009_20(TernaryPodObj) = Load[#temp2009:31] : &:r2009_17, ~m? +# 2009| r2009_21(glval) = VariableAddress[#temp2009:9] : +# 2009| mu2009_22(TernaryPodObj) = Store[#temp2009:9] : &:r2009_21, r2009_20 +#-----| Goto -> Block 7 + +# 2010| Block 10 +# 2010| r2010_8(glval) = VariableAddress[#temp2010:10] : +# 2010| r2010_9(TernaryPodObj) = Load[#temp2010:10] : &:r2010_8, ~m? +# 2010| r2010_10(glval) = VariableAddress[z] : +# 2010| mu2010_11(TernaryPodObj) = Store[z] : &:r2010_10, r2010_9 +# 2010| r2010_12(glval) = CopyValue : r2010_10 +# 2010| mu2010_13(TernaryPodObj) = Store[?] : &:r2010_12, r2010_4 +# 2011| v2011_1(void) = NoOp : +# 2006| v2006_12(void) = ReturnVoid : +# 2006| v2006_13(void) = AliasedUse : ~m? +# 2006| v2006_14(void) = ExitFunction : + +# 2010| Block 11 +# 2010| r2010_14(glval) = VariableAddress[x] : +# 2010| r2010_15(TernaryPodObj) = Load[x] : &:r2010_14, ~m? +# 2010| r2010_16(glval) = VariableAddress[#temp2010:10] : +# 2010| mu2010_17(TernaryPodObj) = Store[#temp2010:10] : &:r2010_16, r2010_15 +#-----| Goto -> Block 10 + +# 2010| Block 12 +# 2010| r2010_18(glval) = VariableAddress[y] : +# 2010| r2010_19(TernaryPodObj) = Load[y] : &:r2010_18, ~m? +# 2010| r2010_20(glval) = VariableAddress[#temp2010:10] : +# 2010| mu2010_21(TernaryPodObj) = Store[#temp2010:10] : &:r2010_20, r2010_19 +#-----| Goto -> Block 10 + +# 2013| TernaryNonPodObj& TernaryNonPodObj::operator=(TernaryNonPodObj const&) +# 2013| Block 0 +# 2013| v2013_1(void) = EnterFunction : +# 2013| mu2013_2(unknown) = AliasedDefinition : +# 2013| mu2013_3(unknown) = InitializeNonLocal : +# 2013| r2013_4(glval) = VariableAddress[#this] : +# 2013| mu2013_5(glval) = InitializeParameter[#this] : &:r2013_4 +# 2013| r2013_6(glval) = Load[#this] : &:r2013_4, ~m? +# 2013| mu2013_7(TernaryNonPodObj) = InitializeIndirection[#this] : &:r2013_6 +#-----| r0_1(glval) = VariableAddress[(unnamed parameter 0)] : +#-----| mu0_2(TernaryNonPodObj &) = InitializeParameter[(unnamed parameter 0)] : &:r0_1 +#-----| r0_3(TernaryNonPodObj &) = Load[(unnamed parameter 0)] : &:r0_1, ~m? +#-----| mu0_4(unknown) = InitializeIndirection[(unnamed parameter 0)] : &:r0_3 +#-----| r0_5(glval) = VariableAddress[#return] : +#-----| r0_6(glval) = VariableAddress[#this] : +#-----| r0_7(TernaryNonPodObj *) = Load[#this] : &:r0_6, ~m? +#-----| r0_8(glval) = CopyValue : r0_7 +#-----| r0_9(TernaryNonPodObj &) = CopyValue : r0_8 +#-----| mu0_10(TernaryNonPodObj &) = Store[#return] : &:r0_5, r0_9 +# 2013| v2013_8(void) = ReturnIndirection[#this] : &:r2013_6, ~m? +#-----| v0_11(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m? +# 2013| r2013_9(glval) = VariableAddress[#return] : +# 2013| v2013_10(void) = ReturnValue : &:r2013_9, ~m? +# 2013| v2013_11(void) = AliasedUse : ~m? +# 2013| v2013_12(void) = ExitFunction : + +# 2013| void TernaryNonPodObj::TernaryNonPodObj() +# 2013| Block 0 +# 2013| v2013_1(void) = EnterFunction : +# 2013| mu2013_2(unknown) = AliasedDefinition : +# 2013| mu2013_3(unknown) = InitializeNonLocal : +# 2013| r2013_4(glval) = VariableAddress[#this] : +# 2013| mu2013_5(glval) = InitializeParameter[#this] : &:r2013_4 +# 2013| r2013_6(glval) = Load[#this] : &:r2013_4, ~m? +# 2013| mu2013_7(TernaryNonPodObj) = InitializeIndirection[#this] : &:r2013_6 +# 2013| v2013_8(void) = NoOp : +# 2013| v2013_9(void) = ReturnIndirection[#this] : &:r2013_6, ~m? +# 2013| v2013_10(void) = ReturnVoid : +# 2013| v2013_11(void) = AliasedUse : ~m? +# 2013| v2013_12(void) = ExitFunction : + +# 2013| void TernaryNonPodObj::TernaryNonPodObj(TernaryNonPodObj const&) +# 2013| Block 0 +# 2013| v2013_1(void) = EnterFunction : +# 2013| mu2013_2(unknown) = AliasedDefinition : +# 2013| mu2013_3(unknown) = InitializeNonLocal : +# 2013| r2013_4(glval) = VariableAddress[#this] : +# 2013| mu2013_5(glval) = InitializeParameter[#this] : &:r2013_4 +# 2013| r2013_6(glval) = Load[#this] : &:r2013_4, ~m? +# 2013| mu2013_7(TernaryNonPodObj) = InitializeIndirection[#this] : &:r2013_6 +#-----| r0_1(glval) = VariableAddress[(unnamed parameter 0)] : +#-----| mu0_2(TernaryNonPodObj &) = InitializeParameter[(unnamed parameter 0)] : &:r0_1 +#-----| r0_3(TernaryNonPodObj &) = Load[(unnamed parameter 0)] : &:r0_1, ~m? +#-----| mu0_4(unknown) = InitializeIndirection[(unnamed parameter 0)] : &:r0_3 +# 2013| v2013_8(void) = NoOp : +# 2013| v2013_9(void) = ReturnIndirection[#this] : &:r2013_6, ~m? +#-----| v0_5(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m? +# 2013| v2013_10(void) = ReturnVoid : +# 2013| v2013_11(void) = AliasedUse : ~m? +# 2013| v2013_12(void) = ExitFunction : + +# 2014| void TernaryNonPodObj::~TernaryNonPodObj() +# 2014| Block 0 +# 2014| v2014_1(void) = EnterFunction : +# 2014| mu2014_2(unknown) = AliasedDefinition : +# 2014| mu2014_3(unknown) = InitializeNonLocal : +# 2014| r2014_4(glval) = VariableAddress[#this] : +# 2014| mu2014_5(glval) = InitializeParameter[#this] : &:r2014_4 +# 2014| r2014_6(glval) = Load[#this] : &:r2014_4, ~m? +# 2014| mu2014_7(TernaryNonPodObj) = InitializeIndirection[#this] : &:r2014_6 +# 2014| v2014_8(void) = NoOp : +# 2014| v2014_9(void) = ReturnIndirection[#this] : &:r2014_6, ~m? +# 2014| v2014_10(void) = ReturnVoid : +# 2014| v2014_11(void) = AliasedUse : ~m? +# 2014| v2014_12(void) = ExitFunction : + +# 2017| void TernaryTestNonPodObj(bool, TernaryNonPodObj, TernaryNonPodObj, TernaryNonPodObj) +# 2017| Block 0 +# 2017| v2017_1(void) = EnterFunction : +# 2017| mu2017_2(unknown) = AliasedDefinition : +# 2017| mu2017_3(unknown) = InitializeNonLocal : +# 2017| r2017_4(glval) = VariableAddress[a] : +# 2017| mu2017_5(bool) = InitializeParameter[a] : &:r2017_4 +# 2017| r2017_6(glval) = VariableAddress[x] : +# 2017| mu2017_7(TernaryNonPodObj) = InitializeParameter[x] : &:r2017_6 +# 2017| r2017_8(glval) = VariableAddress[y] : +# 2017| mu2017_9(TernaryNonPodObj) = InitializeParameter[y] : &:r2017_8 +# 2017| r2017_10(glval) = VariableAddress[z] : +# 2017| mu2017_11(TernaryNonPodObj) = InitializeParameter[z] : &:r2017_10 +# 2018| r2018_1(glval) = VariableAddress[z] : +# 2018| r2018_2(glval) = FunctionAddress[operator=] : +# 2018| r2018_3(glval) = VariableAddress[a] : +# 2018| r2018_4(bool) = Load[a] : &:r2018_3, ~m? +# 2018| v2018_5(void) = ConditionalBranch : r2018_4 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 2018| Block 1 +# 2018| r2018_6(glval) = VariableAddress[#temp2018:9] : +# 2018| r2018_7(glval) = Load[#temp2018:9] : &:r2018_6, ~m? +# 2018| r2018_8(glval) = Convert : r2018_7 +# 2018| r2018_9(TernaryNonPodObj &) = CopyValue : r2018_8 +# 2018| r2018_10(TernaryNonPodObj &) = Call[operator=] : func:r2018_2, this:r2018_1, 0:r2018_9 +# 2018| mu2018_11(unknown) = ^CallSideEffect : ~m? +# 2018| v2018_12(void) = ^IndirectReadSideEffect[-1] : &:r2018_1, ~m? +# 2018| v2018_13(void) = ^BufferReadSideEffect[0] : &:r2018_9, ~m? +# 2018| mu2018_14(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2018_1 +# 2018| r2018_15(glval) = CopyValue : r2018_10 +# 2019| r2019_1(glval) = VariableAddress[z] : +# 2019| r2019_2(glval) = FunctionAddress[operator=] : +# 2019| r2019_3(glval) = VariableAddress[#temp2019:9] : +# 2019| r2019_4(glval) = VariableAddress[a] : +# 2019| r2019_5(bool) = Load[a] : &:r2019_4, ~m? +# 2019| v2019_6(void) = ConditionalBranch : r2019_5 +#-----| False -> Block 6 +#-----| True -> Block 5 + +# 2018| Block 2 +# 2018| r2018_16(glval) = VariableAddress[x] : +# 2018| r2018_17(glval) = VariableAddress[#temp2018:9] : +# 2018| mu2018_18(glval) = Store[#temp2018:9] : &:r2018_17, r2018_16 +#-----| Goto -> Block 1 + +# 2018| Block 3 +# 2018| r2018_19(glval) = VariableAddress[y] : +# 2018| r2018_20(glval) = VariableAddress[#temp2018:9] : +# 2018| mu2018_21(glval) = Store[#temp2018:9] : &:r2018_20, r2018_19 +#-----| Goto -> Block 1 + +# 2019| Block 4 +# 2019| r2019_7(glval) = VariableAddress[#temp2019:9] : +# 2019| r2019_8(TernaryNonPodObj) = Load[#temp2019:9] : &:r2019_7, ~m? +# 2019| mu2019_9(TernaryNonPodObj) = Store[#temp2019:9] : &:r2019_3, r2019_8 +# 2019| r2019_10(glval) = Convert : r2019_3 +# 2019| r2019_11(TernaryNonPodObj &) = CopyValue : r2019_10 +# 2019| r2019_12(TernaryNonPodObj &) = Call[operator=] : func:r2019_2, this:r2019_1, 0:r2019_11 +# 2019| mu2019_13(unknown) = ^CallSideEffect : ~m? +# 2019| v2019_14(void) = ^IndirectReadSideEffect[-1] : &:r2019_1, ~m? +# 2019| v2019_15(void) = ^BufferReadSideEffect[0] : &:r2019_11, ~m? +# 2019| mu2019_16(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2019_1 +# 2019| r2019_17(glval) = CopyValue : r2019_12 +# 2020| r2020_1(glval) = VariableAddress[z] : +# 2020| r2020_2(glval) = FunctionAddress[operator=] : +# 2020| r2020_3(glval) = VariableAddress[#temp2020:9] : +# 2020| r2020_4(glval) = VariableAddress[a] : +# 2020| r2020_5(bool) = Load[a] : &:r2020_4, ~m? +# 2020| v2020_6(void) = ConditionalBranch : r2020_5 +#-----| False -> Block 9 +#-----| True -> Block 8 + +# 2019| Block 5 +# 2019| r2019_18(glval) = VariableAddress[#temp2019:13] : +# 2019| mu2019_19(TernaryNonPodObj) = Uninitialized[#temp2019:13] : &:r2019_18 +# 2019| r2019_20(glval) = FunctionAddress[TernaryNonPodObj] : +# 2019| r2019_21(glval) = VariableAddress[x] : +# 2019| r2019_22(glval) = Convert : r2019_21 +# 2019| r2019_23(TernaryNonPodObj &) = CopyValue : r2019_22 +# 2019| v2019_24(void) = Call[TernaryNonPodObj] : func:r2019_20, this:r2019_18, 0:r2019_23 +# 2019| mu2019_25(unknown) = ^CallSideEffect : ~m? +# 2019| v2019_26(void) = ^BufferReadSideEffect[0] : &:r2019_23, ~m? +# 2019| mu2019_27(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2019_18 +# 2019| r2019_28(TernaryNonPodObj) = Load[#temp2019:13] : &:r2019_18, ~m? +# 2019| r2019_29(glval) = VariableAddress[#temp2019:9] : +# 2019| mu2019_30(TernaryNonPodObj) = Store[#temp2019:9] : &:r2019_29, r2019_28 +#-----| Goto -> Block 4 + +# 2019| Block 6 +# 2019| r2019_31(glval) = VariableAddress[#temp2019:17] : +# 2019| mu2019_32(TernaryNonPodObj) = Uninitialized[#temp2019:17] : &:r2019_31 +# 2019| r2019_33(glval) = FunctionAddress[TernaryNonPodObj] : +# 2019| v2019_34(void) = Call[TernaryNonPodObj] : func:r2019_33, this:r2019_31 +# 2019| mu2019_35(unknown) = ^CallSideEffect : ~m? +# 2019| mu2019_36(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2019_31 +# 2019| r2019_37(TernaryNonPodObj) = Load[#temp2019:17] : &:r2019_31, ~m? +# 2019| r2019_38(glval) = VariableAddress[#temp2019:9] : +# 2019| mu2019_39(TernaryNonPodObj) = Store[#temp2019:9] : &:r2019_38, r2019_37 +#-----| Goto -> Block 4 + +# 2020| Block 7 +# 2020| r2020_7(glval) = VariableAddress[#temp2020:9] : +# 2020| r2020_8(TernaryNonPodObj) = Load[#temp2020:9] : &:r2020_7, ~m? +# 2020| mu2020_9(TernaryNonPodObj) = Store[#temp2020:9] : &:r2020_3, r2020_8 +# 2020| r2020_10(glval) = Convert : r2020_3 +# 2020| r2020_11(TernaryNonPodObj &) = CopyValue : r2020_10 +# 2020| r2020_12(TernaryNonPodObj &) = Call[operator=] : func:r2020_2, this:r2020_1, 0:r2020_11 +# 2020| mu2020_13(unknown) = ^CallSideEffect : ~m? +# 2020| v2020_14(void) = ^IndirectReadSideEffect[-1] : &:r2020_1, ~m? +# 2020| v2020_15(void) = ^BufferReadSideEffect[0] : &:r2020_11, ~m? +# 2020| mu2020_16(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2020_1 +# 2020| r2020_17(glval) = CopyValue : r2020_12 +# 2021| r2021_1(glval) = VariableAddress[z] : +# 2021| r2021_2(glval) = FunctionAddress[operator=] : +# 2021| r2021_3(glval) = VariableAddress[a] : +# 2021| r2021_4(bool) = Load[a] : &:r2021_3, ~m? +# 2021| v2021_5(void) = ConditionalBranch : r2021_4 +#-----| False -> Block 12 +#-----| True -> Block 11 + +# 2020| Block 8 +# 2020| r2020_18(glval) = VariableAddress[#temp2020:13] : +# 2020| mu2020_19(TernaryNonPodObj) = Uninitialized[#temp2020:13] : &:r2020_18 +# 2020| r2020_20(glval) = FunctionAddress[TernaryNonPodObj] : +# 2020| v2020_21(void) = Call[TernaryNonPodObj] : func:r2020_20, this:r2020_18 +# 2020| mu2020_22(unknown) = ^CallSideEffect : ~m? +# 2020| mu2020_23(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2020_18 +# 2020| r2020_24(TernaryNonPodObj) = Load[#temp2020:13] : &:r2020_18, ~m? +# 2020| r2020_25(glval) = VariableAddress[#temp2020:9] : +# 2020| mu2020_26(TernaryNonPodObj) = Store[#temp2020:9] : &:r2020_25, r2020_24 +#-----| Goto -> Block 7 + +# 2020| Block 9 +# 2020| r2020_27(glval) = VariableAddress[#temp2020:34] : +# 2020| mu2020_28(TernaryNonPodObj) = Uninitialized[#temp2020:34] : &:r2020_27 +# 2020| r2020_29(glval) = FunctionAddress[TernaryNonPodObj] : +# 2020| v2020_30(void) = Call[TernaryNonPodObj] : func:r2020_29, this:r2020_27 +# 2020| mu2020_31(unknown) = ^CallSideEffect : ~m? +# 2020| mu2020_32(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2020_27 +# 2020| r2020_33(TernaryNonPodObj) = Load[#temp2020:34] : &:r2020_27, ~m? +# 2020| r2020_34(glval) = VariableAddress[#temp2020:9] : +# 2020| mu2020_35(TernaryNonPodObj) = Store[#temp2020:9] : &:r2020_34, r2020_33 +#-----| Goto -> Block 7 + +# 2021| Block 10 +# 2021| r2021_6(glval) = VariableAddress[#temp2021:10] : +# 2021| r2021_7(glval) = Load[#temp2021:10] : &:r2021_6, ~m? +# 2021| r2021_8(glval) = Convert : r2021_7 +# 2021| r2021_9(TernaryNonPodObj &) = CopyValue : r2021_8 +# 2021| r2021_10(TernaryNonPodObj &) = Call[operator=] : func:r2021_2, this:r2021_1, 0:r2021_9 +# 2021| mu2021_11(unknown) = ^CallSideEffect : ~m? +# 2021| v2021_12(void) = ^IndirectReadSideEffect[-1] : &:r2021_1, ~m? +# 2021| v2021_13(void) = ^BufferReadSideEffect[0] : &:r2021_9, ~m? +# 2021| mu2021_14(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_1 +# 2021| r2021_15(glval) = CopyValue : r2021_10 +# 2021| r2021_16(glval) = FunctionAddress[operator=] : +# 2021| r2021_17(glval) = VariableAddress[#temp2021:23] : +# 2021| mu2021_18(TernaryNonPodObj) = Uninitialized[#temp2021:23] : &:r2021_17 +# 2021| r2021_19(glval) = FunctionAddress[TernaryNonPodObj] : +# 2021| v2021_20(void) = Call[TernaryNonPodObj] : func:r2021_19, this:r2021_17 +# 2021| mu2021_21(unknown) = ^CallSideEffect : ~m? +# 2021| mu2021_22(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_17 +# 2021| r2021_23(glval) = Convert : r2021_17 +# 2021| r2021_24(TernaryNonPodObj &) = CopyValue : r2021_23 +# 2021| r2021_25(TernaryNonPodObj &) = Call[operator=] : func:r2021_16, this:r2021_15, 0:r2021_24 +# 2021| mu2021_26(unknown) = ^CallSideEffect : ~m? +# 2021| v2021_27(void) = ^IndirectReadSideEffect[-1] : &:r2021_15, ~m? +# 2021| v2021_28(void) = ^BufferReadSideEffect[0] : &:r2021_24, ~m? +# 2021| mu2021_29(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_15 +# 2021| r2021_30(glval) = CopyValue : r2021_25 +# 2022| v2022_1(void) = NoOp : +# 2017| v2017_12(void) = ReturnVoid : +# 2017| v2017_13(void) = AliasedUse : ~m? +# 2017| v2017_14(void) = ExitFunction : + +# 2021| Block 11 +# 2021| r2021_31(glval) = VariableAddress[x] : +# 2021| r2021_32(glval) = VariableAddress[#temp2021:10] : +# 2021| mu2021_33(glval) = Store[#temp2021:10] : &:r2021_32, r2021_31 +#-----| Goto -> Block 10 + +# 2021| Block 12 +# 2021| r2021_34(glval) = VariableAddress[y] : +# 2021| r2021_35(glval) = VariableAddress[#temp2021:10] : +# 2021| mu2021_36(glval) = Store[#temp2021:10] : &:r2021_35, r2021_34 +#-----| Goto -> Block 10 + +# 2026| unsigned int CommaTest(unsigned int) +# 2026| Block 0 +# 2026| v2026_1(void) = EnterFunction : +# 2026| mu2026_2(unknown) = AliasedDefinition : +# 2026| mu2026_3(unknown) = InitializeNonLocal : +# 2026| r2026_4(glval) = VariableAddress[x] : +# 2026| mu2026_5(unsigned int) = InitializeParameter[x] : &:r2026_4 +# 2027| r2027_1(glval) = VariableAddress[y] : +# 2027| mu2027_2(unsigned int) = Uninitialized[y] : &:r2027_1 +# 2028| r2028_1(glval) = VariableAddress[x] : +# 2028| r2028_2(unsigned int) = Load[x] : &:r2028_1, ~m? +# 2028| r2028_3(unsigned int) = Constant[100] : +# 2028| r2028_4(bool) = CompareLT : r2028_2, r2028_3 +# 2028| v2028_5(void) = ConditionalBranch : r2028_4 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 2026| Block 1 +# 2026| r2026_6(glval) = VariableAddress[#return] : +# 2026| v2026_7(void) = ReturnValue : &:r2026_6, ~m? +# 2026| v2026_8(void) = AliasedUse : ~m? +# 2026| v2026_9(void) = ExitFunction : + +# 2028| Block 2 +# 2028| r2028_6(glval) = VariableAddress[#temp2028:7] : +# 2028| r2028_7(unsigned int) = Load[#temp2028:7] : &:r2028_6, ~m? +# 2028| r2028_8(glval) = VariableAddress[y] : +# 2028| mu2028_9(unsigned int) = Store[y] : &:r2028_8, r2028_7 +# 2031| v2031_1(void) = Unreached : + +# 2029| Block 3 +# 2029| r2029_1(glval) = FunctionAddress[CommaTestHelper] : +# 2029| r2029_2(glval) = VariableAddress[x] : +# 2029| r2029_3(unsigned int) = Load[x] : &:r2029_2, ~m? +# 2029| v2029_4(void) = Call[CommaTestHelper] : func:r2029_1, 0:r2029_3 +# 2029| mu2029_5(unknown) = ^CallSideEffect : ~m? +# 2029| r2029_6(glval) = VariableAddress[x] : +# 2029| r2029_7(unsigned int) = Load[x] : &:r2029_6, ~m? +# 2029| r2029_8(unsigned int) = CopyValue : r2029_7 +# 2028| r2028_10(glval) = VariableAddress[#temp2028:7] : +# 2028| mu2028_11(unsigned int) = Store[#temp2028:7] : &:r2028_10, r2029_8 +#-----| Goto -> Block 2 + +# 2030| Block 4 +# 2030| r2030_1(glval) = FunctionAddress[CommaTestHelper] : +# 2030| r2030_2(glval) = VariableAddress[x] : +# 2030| r2030_3(unsigned int) = Load[x] : &:r2030_2, ~m? +# 2030| v2030_4(void) = Call[CommaTestHelper] : func:r2030_1, 0:r2030_3 +# 2030| mu2030_5(unknown) = ^CallSideEffect : ~m? +# 2030| r2030_6(int) = Constant[10] : +# 2030| r2030_7(int) = CopyValue : r2030_6 +# 2030| r2030_8(unsigned int) = Convert : r2030_7 +# 2028| r2028_12(glval) = VariableAddress[#temp2028:7] : +# 2028| mu2028_13(unsigned int) = Store[#temp2028:7] : &:r2028_12, r2030_8 +#-----| Goto -> Block 2 + perf-regression.cpp: # 6| void Big::Big() # 6| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql index ae37a4a932b..a40eaead511 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql @@ -7,5 +7,5 @@ private import semmle.code.cpp.ir.implementation.raw.PrintIR private import PrintConfig private class PrintConfig extends PrintIRConfiguration { - override predicate shouldPrintFunction(Declaration decl) { shouldDumpFunction(decl) } + override predicate shouldPrintDeclaration(Declaration decl) { shouldDumpDeclaration(decl) } } diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index ebb54018d31..a357821fcf5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -2157,3 +2157,46 @@ ssa.cpp: # 431| v431_9(void) = ReturnValue : &:r431_8, m435_4 # 431| v431_10(void) = AliasedUse : m431_3 # 431| v431_11(void) = ExitFunction : + +# 438| void Conditional(bool, int, int) +# 438| Block 0 +# 438| v438_1(void) = EnterFunction : +# 438| m438_2(unknown) = AliasedDefinition : +# 438| m438_3(unknown) = InitializeNonLocal : +# 438| m438_4(unknown) = Chi : total:m438_2, partial:m438_3 +# 438| r438_5(glval) = VariableAddress[a] : +# 438| m438_6(bool) = InitializeParameter[a] : &:r438_5 +# 438| r438_7(glval) = VariableAddress[x] : +# 438| m438_8(int) = InitializeParameter[x] : &:r438_7 +# 438| r438_9(glval) = VariableAddress[y] : +# 438| m438_10(int) = InitializeParameter[y] : &:r438_9 +# 439| r439_1(glval) = VariableAddress[z] : +# 439| r439_2(glval) = VariableAddress[a] : +# 439| r439_3(bool) = Load[a] : &:r439_2, m438_6 +# 439| v439_4(void) = ConditionalBranch : r439_3 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 439| Block 1 +# 439| m439_5(int) = Phi : from 2:m439_12, from 3:m439_16 +# 439| r439_6(glval) = VariableAddress[#temp439:13] : +# 439| r439_7(int) = Load[#temp439:13] : &:r439_6, m439_5 +# 439| m439_8(int) = Store[z] : &:r439_1, r439_7 +# 440| v440_1(void) = NoOp : +# 438| v438_11(void) = ReturnVoid : +# 438| v438_12(void) = AliasedUse : m438_3 +# 438| v438_13(void) = ExitFunction : + +# 439| Block 2 +# 439| r439_9(glval) = VariableAddress[x] : +# 439| r439_10(int) = Load[x] : &:r439_9, m438_8 +# 439| r439_11(glval) = VariableAddress[#temp439:13] : +# 439| m439_12(int) = Store[#temp439:13] : &:r439_11, r439_10 +#-----| Goto -> Block 1 + +# 439| Block 3 +# 439| r439_13(glval) = VariableAddress[y] : +# 439| r439_14(int) = Load[y] : &:r439_13, m438_10 +# 439| r439_15(glval) = VariableAddress[#temp439:13] : +# 439| m439_16(int) = Store[#temp439:13] : &:r439_15, r439_14 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index a2390ac28e7..f9eb5b53828 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -2146,3 +2146,46 @@ ssa.cpp: # 431| v431_9(void) = ReturnValue : &:r431_8, m435_4 # 431| v431_10(void) = AliasedUse : m431_3 # 431| v431_11(void) = ExitFunction : + +# 438| void Conditional(bool, int, int) +# 438| Block 0 +# 438| v438_1(void) = EnterFunction : +# 438| m438_2(unknown) = AliasedDefinition : +# 438| m438_3(unknown) = InitializeNonLocal : +# 438| m438_4(unknown) = Chi : total:m438_2, partial:m438_3 +# 438| r438_5(glval) = VariableAddress[a] : +# 438| m438_6(bool) = InitializeParameter[a] : &:r438_5 +# 438| r438_7(glval) = VariableAddress[x] : +# 438| m438_8(int) = InitializeParameter[x] : &:r438_7 +# 438| r438_9(glval) = VariableAddress[y] : +# 438| m438_10(int) = InitializeParameter[y] : &:r438_9 +# 439| r439_1(glval) = VariableAddress[z] : +# 439| r439_2(glval) = VariableAddress[a] : +# 439| r439_3(bool) = Load[a] : &:r439_2, m438_6 +# 439| v439_4(void) = ConditionalBranch : r439_3 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 439| Block 1 +# 439| m439_5(int) = Phi : from 2:m439_12, from 3:m439_16 +# 439| r439_6(glval) = VariableAddress[#temp439:13] : +# 439| r439_7(int) = Load[#temp439:13] : &:r439_6, m439_5 +# 439| m439_8(int) = Store[z] : &:r439_1, r439_7 +# 440| v440_1(void) = NoOp : +# 438| v438_11(void) = ReturnVoid : +# 438| v438_12(void) = AliasedUse : m438_3 +# 438| v438_13(void) = ExitFunction : + +# 439| Block 2 +# 439| r439_9(glval) = VariableAddress[x] : +# 439| r439_10(int) = Load[x] : &:r439_9, m438_8 +# 439| r439_11(glval) = VariableAddress[#temp439:13] : +# 439| m439_12(int) = Store[#temp439:13] : &:r439_11, r439_10 +#-----| Goto -> Block 1 + +# 439| Block 3 +# 439| r439_13(glval) = VariableAddress[y] : +# 439| r439_14(int) = Load[y] : &:r439_13, m438_10 +# 439| r439_15(glval) = VariableAddress[#temp439:13] : +# 439| m439_16(int) = Store[#temp439:13] : &:r439_15, r439_14 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index fdeeb4ec2ba..56caf9de3b6 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -434,3 +434,7 @@ int noreturnTest2(int x) { } return x; } + +void Conditional(bool a, int x, int y) { + int z = a ? x : y; +} diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index f51e8fef7ac..96b35a76c3b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -2002,3 +2002,45 @@ ssa.cpp: # 431| v431_8(void) = ReturnValue : &:r431_7, m435_4 # 431| v431_9(void) = AliasedUse : ~m? # 431| v431_10(void) = ExitFunction : + +# 438| void Conditional(bool, int, int) +# 438| Block 0 +# 438| v438_1(void) = EnterFunction : +# 438| mu438_2(unknown) = AliasedDefinition : +# 438| mu438_3(unknown) = InitializeNonLocal : +# 438| r438_4(glval) = VariableAddress[a] : +# 438| m438_5(bool) = InitializeParameter[a] : &:r438_4 +# 438| r438_6(glval) = VariableAddress[x] : +# 438| m438_7(int) = InitializeParameter[x] : &:r438_6 +# 438| r438_8(glval) = VariableAddress[y] : +# 438| m438_9(int) = InitializeParameter[y] : &:r438_8 +# 439| r439_1(glval) = VariableAddress[z] : +# 439| r439_2(glval) = VariableAddress[a] : +# 439| r439_3(bool) = Load[a] : &:r439_2, m438_5 +# 439| v439_4(void) = ConditionalBranch : r439_3 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 439| Block 1 +# 439| m439_5(int) = Phi : from 2:m439_12, from 3:m439_16 +# 439| r439_6(glval) = VariableAddress[#temp439:13] : +# 439| r439_7(int) = Load[#temp439:13] : &:r439_6, m439_5 +# 439| m439_8(int) = Store[z] : &:r439_1, r439_7 +# 440| v440_1(void) = NoOp : +# 438| v438_10(void) = ReturnVoid : +# 438| v438_11(void) = AliasedUse : ~m? +# 438| v438_12(void) = ExitFunction : + +# 439| Block 2 +# 439| r439_9(glval) = VariableAddress[x] : +# 439| r439_10(int) = Load[x] : &:r439_9, m438_7 +# 439| r439_11(glval) = VariableAddress[#temp439:13] : +# 439| m439_12(int) = Store[#temp439:13] : &:r439_11, r439_10 +#-----| Goto -> Block 1 + +# 439| Block 3 +# 439| r439_13(glval) = VariableAddress[y] : +# 439| r439_14(int) = Load[y] : &:r439_13, m438_9 +# 439| r439_15(glval) = VariableAddress[#temp439:13] : +# 439| m439_16(int) = Store[#temp439:13] : &:r439_15, r439_14 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index f51e8fef7ac..96b35a76c3b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -2002,3 +2002,45 @@ ssa.cpp: # 431| v431_8(void) = ReturnValue : &:r431_7, m435_4 # 431| v431_9(void) = AliasedUse : ~m? # 431| v431_10(void) = ExitFunction : + +# 438| void Conditional(bool, int, int) +# 438| Block 0 +# 438| v438_1(void) = EnterFunction : +# 438| mu438_2(unknown) = AliasedDefinition : +# 438| mu438_3(unknown) = InitializeNonLocal : +# 438| r438_4(glval) = VariableAddress[a] : +# 438| m438_5(bool) = InitializeParameter[a] : &:r438_4 +# 438| r438_6(glval) = VariableAddress[x] : +# 438| m438_7(int) = InitializeParameter[x] : &:r438_6 +# 438| r438_8(glval) = VariableAddress[y] : +# 438| m438_9(int) = InitializeParameter[y] : &:r438_8 +# 439| r439_1(glval) = VariableAddress[z] : +# 439| r439_2(glval) = VariableAddress[a] : +# 439| r439_3(bool) = Load[a] : &:r439_2, m438_5 +# 439| v439_4(void) = ConditionalBranch : r439_3 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 439| Block 1 +# 439| m439_5(int) = Phi : from 2:m439_12, from 3:m439_16 +# 439| r439_6(glval) = VariableAddress[#temp439:13] : +# 439| r439_7(int) = Load[#temp439:13] : &:r439_6, m439_5 +# 439| m439_8(int) = Store[z] : &:r439_1, r439_7 +# 440| v440_1(void) = NoOp : +# 438| v438_10(void) = ReturnVoid : +# 438| v438_11(void) = AliasedUse : ~m? +# 438| v438_12(void) = ExitFunction : + +# 439| Block 2 +# 439| r439_9(glval) = VariableAddress[x] : +# 439| r439_10(int) = Load[x] : &:r439_9, m438_7 +# 439| r439_11(glval) = VariableAddress[#temp439:13] : +# 439| m439_12(int) = Store[#temp439:13] : &:r439_11, r439_10 +#-----| Goto -> Block 1 + +# 439| Block 3 +# 439| r439_13(glval) = VariableAddress[y] : +# 439| r439_14(int) = Load[y] : &:r439_13, m438_9 +# 439| r439_15(glval) = VariableAddress[#temp439:13] : +# 439| m439_16(int) = Store[#temp439:13] : &:r439_15, r439_14 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/types.expected b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected index 713dc44e495..b73feb125e2 100644 --- a/cpp/ql/test/library-tests/templates/type_instantiations/types.expected +++ b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected @@ -13,7 +13,6 @@ | file://:0:0:0:0 | _Float64 | | file://:0:0:0:0 | _Float64x | | file://:0:0:0:0 | _Float128 | -| file://:0:0:0:0 | _Float128x | | file://:0:0:0:0 | _Imaginary double | | file://:0:0:0:0 | _Imaginary float | | file://:0:0:0:0 | _Imaginary long double | diff --git a/cpp/ql/test/library-tests/type_sizes/type_sizes.expected b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected index a92bbe2f69a..69bfebe2195 100644 --- a/cpp/ql/test/library-tests/type_sizes/type_sizes.expected +++ b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected @@ -33,7 +33,6 @@ | file://:0:0:0:0 | _Float64 | 8 | | file://:0:0:0:0 | _Float64x | 16 | | file://:0:0:0:0 | _Float128 | 16 | -| file://:0:0:0:0 | _Float128x | 32 | | file://:0:0:0:0 | _Imaginary double | 8 | | file://:0:0:0:0 | _Imaginary float | 4 | | file://:0:0:0:0 | _Imaginary long double | 16 | diff --git a/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected b/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected index 3e231649b2c..d635ec8b5df 100644 --- a/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected +++ b/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected @@ -15,7 +15,6 @@ | file://:0:0:0:0 | _Float64 | _Float64 | | file://:0:0:0:0 | _Float64x | _Float64x | | file://:0:0:0:0 | _Float128 | _Float128 | -| file://:0:0:0:0 | _Float128x | _Float128x | | file://:0:0:0:0 | _Imaginary double | _Imaginary double | | file://:0:0:0:0 | _Imaginary float | _Imaginary float | | file://:0:0:0:0 | _Imaginary long double | _Imaginary long double | diff --git a/cpp/ql/test/library-tests/variables/variables/types.expected b/cpp/ql/test/library-tests/variables/variables/types.expected index 19e7411005f..f086dec166a 100644 --- a/cpp/ql/test/library-tests/variables/variables/types.expected +++ b/cpp/ql/test/library-tests/variables/variables/types.expected @@ -14,7 +14,6 @@ | _Float64 | BinaryFloatingPointType, RealNumberType | | | | | | _Float64x | BinaryFloatingPointType, RealNumberType | | | | | | _Float128 | BinaryFloatingPointType, RealNumberType | | | | | -| _Float128x | BinaryFloatingPointType, RealNumberType | | | | | | _Imaginary double | BinaryFloatingPointType, ImaginaryNumberType | | | | | | _Imaginary float | BinaryFloatingPointType, ImaginaryNumberType | | | | | | _Imaginary long double | BinaryFloatingPointType, ImaginaryNumberType | | | | | diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs index bb521523ead..c3ea55c7114 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Standalone public Runtime(IDotNet dotNet) => this.dotNet = dotNet; - internal sealed class RuntimeVersion : IComparable + internal record RuntimeVersion : IComparable { private readonly string dir; private readonly Version version; @@ -71,11 +71,6 @@ namespace Semmle.Extraction.CSharp.Standalone return c; } - public override bool Equals(object? obj) => - obj is not null && obj is RuntimeVersion other && other.FullPath == FullPath; - - public override int GetHashCode() => FullPath.GetHashCode(); - public override string ToString() => FullPath; } @@ -97,7 +92,7 @@ namespace Semmle.Extraction.CSharp.Standalone var match = RuntimeRegex().Match(r); if (match.Success) { - runtimes.AddOrUpdate(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value)); + runtimes.AddOrUpdateToLatest(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value)); } }); diff --git a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs index fa932f0cd9c..5ac4f3bfaa9 100644 --- a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs +++ b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs @@ -22,9 +22,9 @@ namespace Semmle.Util /// /// Adds a new value or replaces the existing value (if the new value is greater than the existing) - /// in dictionary for the given key. + /// in this dictionary for the given key. /// - public static void AddOrUpdate(this Dictionary dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable + public static void AddOrUpdateToLatest(this Dictionary dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable { if (!dict.TryGetValue(key, out var existing) || existing.CompareTo(value) < 0) { diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index 3de1098514d..1eeadc75491 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.2 + +No user-facing changes. + ## 1.6.1 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.2.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.2.md new file mode 100644 index 00000000000..bbe3747556f --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.2.md @@ -0,0 +1,3 @@ +## 1.6.2 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml index ef7a789e0cf..5f5beb68311 100644 --- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.6.1 +lastReleaseVersion: 1.6.2 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 327f33fd5b3..4848c59ae3b 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.6.1 +version: 1.6.2 groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index 3de1098514d..1eeadc75491 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.2 + +No user-facing changes. + ## 1.6.1 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.2.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.2.md new file mode 100644 index 00000000000..bbe3747556f --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.2.md @@ -0,0 +1,3 @@ +## 1.6.2 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml index ef7a789e0cf..5f5beb68311 100644 --- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.6.1 +lastReleaseVersion: 1.6.2 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index cb168a76984..836f625253c 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.6.1 +version: 1.6.2 groups: - csharp - solorigate diff --git a/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/Program.cs b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/Program.cs new file mode 100644 index 00000000000..e9708d0b5d2 --- /dev/null +++ b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/Program.cs @@ -0,0 +1 @@ +Console.WriteLine(args[0]); diff --git a/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/dotnet_build.csproj b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/dotnet_build.csproj new file mode 100644 index 00000000000..f02677bf640 --- /dev/null +++ b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/dotnet_build.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + diff --git a/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/test.py b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/test.py new file mode 100644 index 00000000000..0f2584104eb --- /dev/null +++ b/csharp/ql/integration-tests/all-platforms/dotnet_no_args_inject/test.py @@ -0,0 +1,9 @@ +from create_database_utils import * +from diagnostics_test_utils import * + +# the tracer configuration should not inject the extra command-line arguments for these commands +# and they should therefore run successfully +run_codeql_database_init(lang="csharp") +# this command fails on Windows for some reason, so we comment it out for now +# run_codeql_database_trace_command(['dotnet', 'tool', 'search', 'publish']) +run_codeql_database_trace_command(['dotnet', 'new', 'console', '--force', '--name', 'build', '--output', '.']) diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index b2a792d29a9..7806ffed612 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.2 + +No user-facing changes. + ## 0.7.1 ### New Features diff --git a/csharp/ql/lib/change-notes/released/0.7.2.md b/csharp/ql/lib/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8693d609ec7 --- /dev/null +++ b/csharp/ql/lib/change-notes/released/0.7.2.md @@ -0,0 +1,3 @@ +## 0.7.2 + +No user-facing changes. diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 414e410efa5..3180b6d3f5d 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/csharp-all -version: 0.7.1 +version: 0.7.2 groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp library: true upgrades: upgrades dependencies: + codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlow.qll index 847aa850d35..17e698dce2d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlow.qll @@ -6,6 +6,8 @@ import csharp module DataFlow { - import semmle.code.csharp.dataflow.internal.DataFlow + private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.code.csharp.dataflow.internal.DataFlowImpl1 } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index cb26a1c2075..33996578597 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -154,17 +154,17 @@ private module DispatchImpl { * call is a delegate call, or if the qualifier accesses a parameter of * the enclosing callable `c` (including the implicit `this` parameter). */ - predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, DataFlowCallable c) { + predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { c = call.getEnclosingCallable() and - call.getDispatchCall().mayBenefitFromCallContext() + call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext() } /** * Gets a viable dispatch target of `call` in the context `ctx`. This is * restricted to those `call`s for which a context might make a difference. */ - DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) { - exists(DispatchCall dc | dc = call.getDispatchCall() | + DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + exists(DispatchCall dc | dc = call.(NonDelegateDataFlowCall).getDispatchCall() | result.getUnderlyingCallable() = getCallableForDataFlow(dc.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall) .getDispatchCall()).getUnboundDeclaration()) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..4cf39afc812 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index b0de9745816..1975ac9781f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index b0de9745816..1975ac9781f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index b0de9745816..1975ac9781f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..55829d7d059 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll index 2885c307c4e..4c003584d10 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides C#-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,12 @@ module Private { module Public { import DataFlowPublic } + +module CsharpDataFlow implements InputSig { + import Private + import Public + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } + + predicate accessPathLimit = Private::accessPathLimit/0; +} diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 872718d93d5..58f923623ee 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -22,11 +22,13 @@ private import semmle.code.cil.internal.SsaImpl as CilSsaImpl private import codeql.util.Unit /** Gets the callable in which this node occurs. */ -DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallableImpl() } +DataFlowCallable nodeGetEnclosingCallable(Node n) { + result = n.(NodeImpl).getEnclosingCallableImpl() +} /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ -predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) { - p.isParameterOf(c, pos) +predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + p.(ParameterNodeImpl).isParameterOf(c, pos) } /** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */ @@ -1739,7 +1741,7 @@ private PropertyContent getResultContent() { * Holds if data can flow from `node1` to `node2` via an assignment to * content `c`. */ -predicate storeStep(Node node1, Content c, Node node2) { +predicate storeStep(Node node1, ContentSet c, Node node2) { exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | hasNodePath(x, node1, node) and if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 @@ -1840,7 +1842,7 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration /** * Holds if data can flow from `node1` to `node2` via a read of content `c`. */ -predicate readStep(Node node1, Content c, Node node2) { +predicate readStep(Node node1, ContentSet c, Node node2) { exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) and fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) @@ -1901,7 +1903,7 @@ predicate readStep(Node node1, Content c, Node node2) { * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { fieldOrPropertyStore(_, c, _, n.asExpr(), true) or fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) @@ -1949,10 +1951,7 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { class DataFlowType = Gvn::GvnType; /** Gets the type of `n` used for type pruning. */ -pragma[inline] -Gvn::GvnType getNodeType(NodeImpl n) { - pragma[only_bind_into](result) = pragma[only_bind_out](n).getDataFlowType() -} +Gvn::GvnType getNodeType(Node n) { result = n.(NodeImpl).getDataFlowType() } /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } @@ -2140,12 +2139,6 @@ class CastNode extends Node { } } -/** - * Holds if `n` should never be skipped over in the `PathGraph` and in path - * explanations. - */ -predicate neverSkipInPathGraph(Node n) { none() } - class DataFlowExpr = DotNet::Expr; /** Holds if `e` is an expression that always has the same Boolean value `val`. */ @@ -2188,8 +2181,8 @@ predicate forceHighPrecision(Content c) { c instanceof ElementContent } class LambdaCallKind = Unit; /** Holds if `creation` is an expression that creates a delegate for `c`. */ -predicate lambdaCreation(ExprNode creation, LambdaCallKind kind, DataFlowCallable c) { - exists(Expr e | e = creation.getExpr() | +predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { + exists(Expr e | e = creation.asExpr() | c.asCallable() = [ e.(AnonymousFunctionExpr), e.(CallableAccess).getTarget().getUnboundDeclaration(), diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index 74584ff4772..99b5b5e5c3c 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.2 + +No user-facing changes. + ## 0.7.1 No user-facing changes. diff --git a/csharp/ql/src/change-notes/released/0.7.2.md b/csharp/ql/src/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8693d609ec7 --- /dev/null +++ b/csharp/ql/src/change-notes/released/0.7.2.md @@ -0,0 +1,3 @@ +## 0.7.2 + +No user-facing changes. diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index b9106a7bfc7..c4b18d9cb61 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ private import internal.IRInternal @@ -16,7 +16,7 @@ import Imports::IRConfiguration private newtype TPrintIRConfiguration = MkPrintIRConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { /** Gets a textual representation of this configuration. */ @@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration { /** * Holds if the IR for `func` should be printed. By default, holds for all - * functions. + * functions, global and namespace variables, and static local variables. */ - predicate shouldPrintFunction(Language::Declaration decl) { any() } + predicate shouldPrintDeclaration(Language::Declaration decl) { any() } } /** @@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration { */ private class FilteredIRConfiguration extends IRConfiguration { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { - shouldPrintFunction(func) + shouldPrintDeclaration(func) } } -private predicate shouldPrintFunction(Language::Declaration decl) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) +private predicate shouldPrintDeclaration(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) } private predicate shouldPrintInstruction(Instruction i) { @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) { } private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableInstruction(Instruction instr) { - shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction()) + shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index b9106a7bfc7..c4b18d9cb61 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -4,8 +4,8 @@ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * uses, however, it is better to write a query that imports `PrintIR.qll`, extends - * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to - * dump. + * `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations + * to dump. */ private import internal.IRInternal @@ -16,7 +16,7 @@ import Imports::IRConfiguration private newtype TPrintIRConfiguration = MkPrintIRConfiguration() /** - * The query can extend this class to control which functions are printed. + * The query can extend this class to control which declarations are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { /** Gets a textual representation of this configuration. */ @@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration { /** * Holds if the IR for `func` should be printed. By default, holds for all - * functions. + * functions, global and namespace variables, and static local variables. */ - predicate shouldPrintFunction(Language::Declaration decl) { any() } + predicate shouldPrintDeclaration(Language::Declaration decl) { any() } } /** @@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration { */ private class FilteredIRConfiguration extends IRConfiguration { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { - shouldPrintFunction(func) + shouldPrintDeclaration(func) } } -private predicate shouldPrintFunction(Language::Declaration decl) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) +private predicate shouldPrintDeclaration(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) } private predicate shouldPrintInstruction(Instruction i) { @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) { } private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableInstruction(Instruction instr) { - shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction()) + shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) } /** diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 4df6fd6a92d..c4ee4a2b229 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.7.1 +version: 0.7.2 groups: - csharp - queries diff --git a/csharp/ql/test/query-tests/Architecture/Dependencies/MutualDependency/options b/csharp/ql/test/query-tests/Architecture/Dependencies/MutualDependency/options index c281ba1ee1f..a5ea8b797c5 100644 --- a/csharp/ql/test/query-tests/Architecture/Dependencies/MutualDependency/options +++ b/csharp/ql/test/query-tests/Architecture/Dependencies/MutualDependency/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Linq.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/FeatureEnvy/options b/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/FeatureEnvy/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/FeatureEnvy/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/InappropriateIntimacy/options b/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/InappropriateIntimacy/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Architecture/Refactoring Opportunities/InappropriateIntimacy/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Comments/CommentedOutCode/options b/csharp/ql/test/query-tests/Bad Practices/Comments/CommentedOutCode/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Comments/CommentedOutCode/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Comments/TodoComments/options b/csharp/ql/test/query-tests/Bad Practices/Comments/TodoComments/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Comments/TodoComments/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/options b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/options index 8f4f554493e..a5ea8b797c5 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/options +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Threading.Thread.dll /r:System.Diagnostics.Debug.dll /langversion:preview +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Declarations/EmptyInterface/options b/csharp/ql/test/query-tests/Bad Practices/Declarations/EmptyInterface/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Declarations/EmptyInterface/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Declarations/LocalScopeVariableShadowsMember/options b/csharp/ql/test/query-tests/Bad Practices/Declarations/LocalScopeVariableShadowsMember/options index aeb895a4979..6922ae27fca 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Declarations/LocalScopeVariableShadowsMember/options +++ b/csharp/ql/test/query-tests/Bad Practices/Declarations/LocalScopeVariableShadowsMember/options @@ -1 +1,3 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj semmle-extractor-options: ${testdir}/../../../../resources/stubs/System.Windows.cs diff --git a/csharp/ql/test/query-tests/Bad Practices/Declarations/NoConstantsOnly/options b/csharp/ql/test/query-tests/Bad Practices/Declarations/NoConstantsOnly/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Declarations/NoConstantsOnly/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Declarations/TooManyRefParameters/options b/csharp/ql/test/query-tests/Bad Practices/Declarations/TooManyRefParameters/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Declarations/TooManyRefParameters/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/EmptyCatchBlock/options b/csharp/ql/test/query-tests/Bad Practices/EmptyCatchBlock/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/EmptyCatchBlock/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/AbstractToConcreteCollection/options b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/AbstractToConcreteCollection/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/AbstractToConcreteCollection/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/ExposeRepresentation/options b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/ExposeRepresentation/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/ExposeRepresentation/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/StaticArray/options b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/StaticArray/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Implementation Hiding/StaticArray/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/ConfusingMethodNames/options b/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/ConfusingMethodNames/options new file mode 100644 index 00000000000..a5ea8b797c5 --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/ConfusingMethodNames/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/VariableNameTooShort/options b/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/VariableNameTooShort/options index c281ba1ee1f..a5ea8b797c5 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/VariableNameTooShort/options +++ b/csharp/ql/test/query-tests/Bad Practices/Naming Conventions/VariableNameTooShort/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Linq.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/options b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/CSI/CompareIdenticalValues/options b/csharp/ql/test/query-tests/CSI/CompareIdenticalValues/options index 993b83a0589..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/CSI/CompareIdenticalValues/options +++ b/csharp/ql/test/query-tests/CSI/CompareIdenticalValues/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Diagnostics.Debug.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/FutileSyncOnField/options b/csharp/ql/test/query-tests/Concurrency/FutileSyncOnField/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Concurrency/FutileSyncOnField/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/LockOrder/options b/csharp/ql/test/query-tests/Concurrency/LockOrder/options index 28abf783565..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/Concurrency/LockOrder/options +++ b/csharp/ql/test/query-tests/Concurrency/LockOrder/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.Threading.dll /r:System.Threading.Thread.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/LockThis/options b/csharp/ql/test/query-tests/Concurrency/LockThis/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Concurrency/LockThis/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/LockedWait/options b/csharp/ql/test/query-tests/Concurrency/LockedWait/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Concurrency/LockedWait/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/SynchSetUnsynchGet/options b/csharp/ql/test/query-tests/Concurrency/SynchSetUnsynchGet/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Concurrency/SynchSetUnsynchGet/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/UnsafeLazyInitialization/options b/csharp/ql/test/query-tests/Concurrency/UnsafeLazyInitialization/options index 23a607518d8..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/Concurrency/UnsafeLazyInitialization/options +++ b/csharp/ql/test/query-tests/Concurrency/UnsafeLazyInitialization/options @@ -1 +1,2 @@ -semmle-extractor-options: -langversion:latest +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Concurrency/UnsynchronizedStaticAccess/options b/csharp/ql/test/query-tests/Concurrency/UnsynchronizedStaticAccess/options index be589a1b3db..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/Concurrency/UnsynchronizedStaticAccess/options +++ b/csharp/ql/test/query-tests/Concurrency/UnsynchronizedStaticAccess/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Collections.Concurrent.dll /r:System.Threading.Thread.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Configuration/PasswordInConfigurationFile/options b/csharp/ql/test/query-tests/Configuration/PasswordInConfigurationFile/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/Configuration/PasswordInConfigurationFile/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/tools/tracing-config.lua b/csharp/tools/tracing-config.lua index f04169caff5..68b7b816ffe 100644 --- a/csharp/tools/tracing-config.lua +++ b/csharp/tools/tracing-config.lua @@ -24,6 +24,10 @@ function RegisterExtractorPack(id) local testMatch = false local dotnetRunNeedsSeparator = false; local dotnetRunInjectionIndex = nil; + -- A flag indicating whether we are in a position where we expect a sub-command such as `build`. + -- Once we have found one, we set this to `false` to not accidentally pick up on things that + -- look like sub-command names later on in the argument vector. + local inSubCommandPosition = true; local argv = compilerArguments.argv if OperatingSystem == 'windows' then -- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments @@ -35,30 +39,38 @@ function RegisterExtractorPack(id) -- dotnet options start with either - or / (both are legal) local firstCharacter = string.sub(arg, 1, 1) if not (firstCharacter == '-') and not (firstCharacter == '/') then - if (not match) then + if (not match) and inSubCommandPosition then Log(1, 'Dotnet subcommand detected: %s', arg) end - if arg == 'build' or arg == 'msbuild' or arg == 'publish' or arg == 'pack' then - match = true - break - end - if arg == 'run' then - -- for `dotnet run`, we need to make sure that `-p:UseSharedCompilation=false` is - -- not passed in as an argument to the program that is run - match = true - dotnetRunNeedsSeparator = true - dotnetRunInjectionIndex = i + 1 - end - if arg == 'test' then - match = true - testMatch = true + -- only respond to strings that look like sub-command names if we have not yet + -- encountered something that looks like a sub-command + if inSubCommandPosition then + if arg == 'build' or arg == 'msbuild' or arg == 'publish' or arg == 'pack' then + match = true + break + end + if arg == 'run' then + -- for `dotnet run`, we need to make sure that `-p:UseSharedCompilation=false` is + -- not passed in as an argument to the program that is run + match = true + dotnetRunNeedsSeparator = true + dotnetRunInjectionIndex = i + 1 + end + if arg == 'test' then + match = true + testMatch = true + end end + -- for `dotnet test`, we should not append `-p:UseSharedCompilation=false` to the command line -- if an `exe` or `dll` is passed as an argument as the call is forwarded to vstest. if testMatch and (arg:match('%.exe$') or arg:match('%.dll')) then match = false break end + + -- we have found a sub-command, ignore all strings that look like sub-command names from now on + inSubCommandPosition = false end -- if we see a separator to `dotnet run`, inject just prior to the existing separator if arg == '--' then diff --git a/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst b/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst index 02c9d448ffa..dad191274de 100644 --- a/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst +++ b/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst @@ -85,7 +85,6 @@ For each repository, you can see: - Visibility of the repository - Whether analysis is still running (black, moving circle) or finished (green checkmark) - Number of stars the repository has on GitHub -- When the repository was last updated To see the results for a repository: diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp-new.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp-new.rst index 911c930458e..003bc8c0aef 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp-new.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp-new.rst @@ -2,7 +2,7 @@ .. pull-quote:: Note - The data flow library described here is available from CodeQL 2.12.5 onwards. For information on the previous version of the library, see :ref:`Analyzing data flow in C and C++ `. + The data flow library described here is available from CodeQL 2.12.5 onwards. With the release of CodeQL 2.13.0 the library uses the new modular API for data flow. For information on the previous version of the library, see :ref:`Analyzing data flow in C and C++ ` and for information about the new modular API and how to migrate any existing queries to the updated data flow library, see `New dataflow API for CodeQL query writing `__. Analyzing data flow in C and C++ (new) ====================================== @@ -168,74 +168,61 @@ Global data flow tracks data flow throughout the entire program, and is therefor Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -The global data flow library is used by extending the class ``DataFlow::Configuration`` as follows: +The global data flow library is used by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global`` as follows: .. code-block:: ql import semmle.code.cpp.dataflow.new.DataFlow - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + The following predicates are defined in the configuration: - ``isSource``—defines where data may flow from - ``isSink``—defines where data may flow to - ``isBarrier``—optional, restricts the data flow -- ``isBarrierGuard``—optional, restricts the data flow - ``isAdditionalFlowStep``—optional, adds additional flow steps -The characteristic predicate ``MyDataFlowConfiguration()`` defines the name of the configuration, so ``"MyDataFlowConfiguration"`` should be replaced by the name of your class. - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Data flow to $@.", sink, sink.toString() Using global taint tracking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by extending the class ``TaintTracking::Configuration`` as follows: +Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global`` as follows: .. code-block:: ql import semmle.code.cpp.dataflow.new.TaintTracking - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -The following predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource``—defines where taint may flow from -- ``isSink``—defines where taint may flow to -- ``isSanitizer``—optional, restricts the taint flow -- ``isSanitizerGuard``—optional, restricts the taint flow -- ``isAdditionalTaintStep``—optional, adds additional taint steps - -Similar to global data flow, the characteristic predicate ``MyTaintTrackingConfiguration()`` defines the unique name of the configuration, so ``"MyTaintTrackingConfiguration"`` should be replaced by the name of your class. - -The taint tracking analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Examples ~~~~~~~~ @@ -247,17 +234,15 @@ The following data flow configuration tracks data flow from environment variable import cpp import semmle.code.cpp.dataflow.new.DataFlow - class EnvironmentToFileConfiguration extends DataFlow::Configuration { - EnvironmentToFileConfiguration() { this = "EnvironmentToFileConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module EnvironmentToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Function getenv | source.asIndirectExpr(1).(FunctionCall).getTarget() = getenv and getenv.hasGlobalName("getenv") ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc | sink.asIndirectExpr(1) = fc.getArgument(0) and fc.getTarget().hasGlobalName("fopen") @@ -265,16 +250,17 @@ The following data flow configuration tracks data flow from environment variable } } + module EnvironmentToFileFlow = DataFlow::Global; + from - Expr getenv, Expr fopen, EnvironmentToFileConfiguration config, DataFlow::Node source, - DataFlow::Node sink + Expr getenv, Expr fopen, DataFlow::Node source, DataFlow::Node sink where source.asIndirectExpr(1) = getenv and sink.asIndirectExpr(1) = fopen and - config.hasFlow(source, sink) + EnvironmentToFileFlow::flow(source, sink) select fopen, "This 'fopen' uses data from $@.", getenv, "call to 'getenv'" -The following taint-tracking configuration tracks data from a call to ``ntohl`` to an array index operation. It uses the ``Guards`` library to recognize expressions that have been bounds-checked, and defines ``isSanitizer`` to prevent taint from propagating through them. It also uses ``isAdditionalTaintStep`` to add flow from loop bounds to loop indexes. +The following taint-tracking configuration tracks data from a call to ``ntohl`` to an array index operation. It uses the ``Guards`` library to recognize expressions that have been bounds-checked, and defines ``isBarrier`` to prevent taint from propagating through them. It also uses ``isAdditionalFlowStep`` to add flow from loop bounds to loop indexes. .. code-block:: ql @@ -282,18 +268,16 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` import semmle.code.cpp.controlflow.Guards import semmle.code.cpp.dataflow.new.TaintTracking - class NetworkToBufferSizeConfiguration extends TaintTracking::Configuration { - NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" } - - override predicate isSource(DataFlow::Node node) { + module NetworkToBufferSizeConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.asExpr().(FunctionCall).getTarget().hasGlobalName("ntohl") } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { exists(ArrayExpr ae | node.asExpr() = ae.getArrayOffset()) } - override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { exists(Loop loop, LoopCounter lc | loop = lc.getALoop() and loop.getControllingExpr().(RelationalOperation).getGreaterOperand() = pred.asExpr() @@ -302,7 +286,7 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` ) } - override predicate isSanitizer(DataFlow::Node node) { + predicate isBarrier(DataFlow::Node node) { exists(GuardCondition gc, Variable v | gc.getAChild*() = v.getAnAccess() and node.asExpr() = v.getAnAccess() and @@ -312,8 +296,10 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` } } - from DataFlow::Node ntohl, DataFlow::Node offset, NetworkToBufferSizeConfiguration conf - where conf.hasFlow(ntohl, offset) + module NetworkToBufferSizeFlow = TaintTracking::Global; + + from DataFlow::Node ntohl, DataFlow::Node offset + where NetworkToBufferSizeFlow::flow(ntohl, offset) select offset, "This array offset may be influenced by $@.", ntohl, "converted data from the network" @@ -353,14 +339,12 @@ Exercise 2 import cpp import semmle.code.cpp.dataflow.new.DataFlow - class LiteralToGethostbynameConfiguration extends DataFlow::Configuration { - LiteralToGethostbynameConfiguration() { this = "LiteralToGethostbynameConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module LiteralToGethostbynameConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asIndirectExpr(1) instanceof StringLiteral } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc | sink.asIndirectExpr(1) = fc.getArgument(0) and fc.getTarget().hasName("gethostbyname") @@ -368,13 +352,14 @@ Exercise 2 } } + module LiteralToGethostbynameFlow = DataFlow::Global; + from - StringLiteral sl, FunctionCall fc, LiteralToGethostbynameConfiguration cfg, DataFlow::Node source, - DataFlow::Node sink + StringLiteral sl, FunctionCall fc, DataFlow::Node source, DataFlow::Node sink where source.asIndirectExpr(1) = sl and sink.asIndirectExpr(1) = fc.getArgument(0) and - cfg.hasFlow(source, sink) + LiteralToGethostbynameFlow::flow(source, sink) select sl, fc Exercise 3 @@ -401,12 +386,10 @@ Exercise 4 GetenvSource() { this.asIndirectExpr(1).(FunctionCall).getTarget().hasGlobalName("getenv") } } - class GetenvToGethostbynameConfiguration extends DataFlow::Configuration { - GetenvToGethostbynameConfiguration() { this = "GetenvToGethostbynameConfiguration" } + module GetenvToGethostbynameConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof GetenvSource } - override predicate isSource(DataFlow::Node source) { source instanceof GetenvSource } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc | sink.asIndirectExpr(1) = fc.getArgument(0) and fc.getTarget().hasName("gethostbyname") @@ -414,13 +397,14 @@ Exercise 4 } } + module GetenvToGethostbynameFlow = DataFlow::Global; + from - Expr getenv, FunctionCall fc, GetenvToGethostbynameConfiguration cfg, DataFlow::Node source, - DataFlow::Node sink + Expr getenv, FunctionCall fc, DataFlow::Node source, DataFlow::Node sink where source.asIndirectExpr(1) = getenv and sink.asIndirectExpr(1) = fc.getArgument(0) and - cfg.hasFlow(source, sink) + GetenvToGethostbynameFlow::flow(source, sink) select getenv, fc Further reading @@ -430,4 +414,4 @@ Further reading .. include:: ../reusables/cpp-further-reading.rst -.. include:: ../reusables/codeql-ref-tools-further-reading.rst \ No newline at end of file +.. include:: ../reusables/codeql-ref-tools-further-reading.rst diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp.rst index 0776834d243..f9ed5af0db8 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-cpp.rst @@ -2,7 +2,7 @@ .. pull-quote:: Note - The data flow library used in this article has been replaced with an improved library which is available from CodeQL 2.12.5 onwards, see :ref:`Analyzing data flow in C and C++ (new) `. The old library has been deprecated in CodeQL 2.14.1 and will be removed in a later release. + The data flow library used in this article has been replaced with an improved library which is available from CodeQL 2.12.5 onwards, see :ref:`Analyzing data flow in C and C++ (new) `. The old library has been deprecated in CodeQL 2.14.1 and will be removed in a later release. With the release of CodeQL 2.13.0 both libraries use the new modular API for data flow. Analyzing data flow in C and C++ ================================ @@ -152,74 +152,62 @@ Global data flow tracks data flow throughout the entire program, and is therefor Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -The global data flow library is used by extending the class ``DataFlow::Configuration`` as follows: +The global data flow library is used by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global`` as follows: .. code-block:: ql import semmle.code.cpp.dataflow.DataFlow - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + + The following predicates are defined in the configuration: - ``isSource``—defines where data may flow from - ``isSink``—defines where data may flow to - ``isBarrier``—optional, restricts the data flow -- ``isBarrierGuard``—optional, restricts the data flow - ``isAdditionalFlowStep``—optional, adds additional flow steps -The characteristic predicate ``MyDataFlowConfiguration()`` defines the name of the configuration, so ``"MyDataFlowConfiguration"`` should be replaced by the name of your class. - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Data flow to $@.", sink, sink.toString() Using global taint tracking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by extending the class ``TaintTracking::Configuration`` as follows: +Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global`` as follows: .. code-block:: ql import semmle.code.cpp.dataflow.TaintTracking - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -The following predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource``—defines where taint may flow from -- ``isSink``—defines where taint may flow to -- ``isSanitizer``—optional, restricts the taint flow -- ``isSanitizerGuard``—optional, restricts the taint flow -- ``isAdditionalTaintStep``—optional, adds additional taint steps - -Similar to global data flow, the characteristic predicate ``MyTaintTrackingConfiguration()`` defines the unique name of the configuration, so ``"MyTaintTrackingConfiguration"`` should be replaced by the name of your class. - -The taint tracking analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Examples ~~~~~~~~ @@ -230,17 +218,15 @@ The following data flow configuration tracks data flow from environment variable import semmle.code.cpp.dataflow.DataFlow - class EnvironmentToFileConfiguration extends DataFlow::Configuration { - EnvironmentToFileConfiguration() { this = "EnvironmentToFileConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module EnvironmentToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists (Function getenv | source.asExpr().(FunctionCall).getTarget() = getenv and getenv.hasGlobalName("getenv") ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists (FunctionCall fc | sink.asExpr() = fc.getArgument(0) and fc.getTarget().hasGlobalName("fopen") @@ -248,12 +234,14 @@ The following data flow configuration tracks data flow from environment variable } } - from Expr getenv, Expr fopen, EnvironmentToFileConfiguration config - where config.hasFlow(DataFlow::exprNode(getenv), DataFlow::exprNode(fopen)) + module EnvironmentToFileFlow = DataFlow::Global; + + from Expr getenv, Expr fopen + where EnvironmentToFileFlow::flow(DataFlow::exprNode(getenv), DataFlow::exprNode(fopen)) select fopen, "This 'fopen' uses data from $@.", getenv, "call to 'getenv'" -The following taint-tracking configuration tracks data from a call to ``ntohl`` to an array index operation. It uses the ``Guards`` library to recognize expressions that have been bounds-checked, and defines ``isSanitizer`` to prevent taint from propagating through them. It also uses ``isAdditionalTaintStep`` to add flow from loop bounds to loop indexes. +The following taint-tracking configuration tracks data from a call to ``ntohl`` to an array index operation. It uses the ``Guards`` library to recognize expressions that have been bounds-checked, and defines ``isBarrier`` to prevent taint from propagating through them. It also uses ``isAdditionalFlowStep`` to add flow from loop bounds to loop indexes. .. code-block:: ql @@ -261,18 +249,16 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` import semmle.code.cpp.controlflow.Guards import semmle.code.cpp.dataflow.TaintTracking - class NetworkToBufferSizeConfiguration extends TaintTracking::Configuration { - NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" } - - override predicate isSource(DataFlow::Node node) { + module NetworkToBufferSizeConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.asExpr().(FunctionCall).getTarget().hasGlobalName("ntohl") } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { exists(ArrayExpr ae | node.asExpr() = ae.getArrayOffset()) } - override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { exists(Loop loop, LoopCounter lc | loop = lc.getALoop() and loop.getControllingExpr().(RelationalOperation).getGreaterOperand() = pred.asExpr() | @@ -280,7 +266,7 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` ) } - override predicate isSanitizer(DataFlow::Node node) { + predicate isBarrier(DataFlow::Node node) { exists(GuardCondition gc, Variable v | gc.getAChild*() = v.getAnAccess() and node.asExpr() = v.getAnAccess() and @@ -289,8 +275,10 @@ The following taint-tracking configuration tracks data from a call to ``ntohl`` } } - from DataFlow::Node ntohl, DataFlow::Node offset, NetworkToBufferSizeConfiguration conf - where conf.hasFlow(ntohl, offset) + module NetworkToBufferSizeFlow = TaintTracking::Global; + + from DataFlow::Node ntohl, DataFlow::Node offset + where NetworkToBufferSizeFlow::flow(ntohl, offset) select offset, "This array offset may be influenced by $@.", ntohl, "converted data from the network" @@ -327,24 +315,22 @@ Exercise 2 import semmle.code.cpp.dataflow.DataFlow - class LiteralToGethostbynameConfiguration extends DataFlow::Configuration { - LiteralToGethostbynameConfiguration() { - this = "LiteralToGethostbynameConfiguration" - } - - override predicate isSource(DataFlow::Node source) { + module LiteralToGethostbynameConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof StringLiteral } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists (FunctionCall fc | sink.asExpr() = fc.getArgument(0) and fc.getTarget().hasName("gethostbyname")) } } - from StringLiteral sl, FunctionCall fc, LiteralToGethostbynameConfiguration cfg - where cfg.hasFlow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0))) + module LiteralToGethostbynameFlow = DataFlow::Global; + + from StringLiteral sl, FunctionCall fc + where LiteralToGethostbynameFlow::flow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0))) select sl, fc Exercise 3 @@ -373,24 +359,22 @@ Exercise 4 } } - class GetenvToGethostbynameConfiguration extends DataFlow::Configuration { - GetenvToGethostbynameConfiguration() { - this = "GetenvToGethostbynameConfiguration" - } - - override predicate isSource(DataFlow::Node source) { + module GetenvToGethostbynameConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof GetenvSource } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists (FunctionCall fc | sink.asExpr() = fc.getArgument(0) and fc.getTarget().hasName("gethostbyname")) } } - from DataFlow::Node getenv, FunctionCall fc, GetenvToGethostbynameConfiguration cfg - where cfg.hasFlow(getenv, DataFlow::exprNode(fc.getArgument(0))) + module GetenvToGethostbynameFlow = DataFlow::Global; + + from DataFlow::Node getenv, FunctionCall fc + where GetenvToGethostbynameFlow::flow(getenv, DataFlow::exprNode(fc.getArgument(0))) select getenv.asExpr(), fc Further reading @@ -400,4 +384,4 @@ Further reading .. include:: ../reusables/cpp-further-reading.rst -.. include:: ../reusables/codeql-ref-tools-further-reading.rst \ No newline at end of file +.. include:: ../reusables/codeql-ref-tools-further-reading.rst diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-csharp.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-csharp.rst index 4d715b7313c..61eb803bdc9 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-csharp.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-csharp.rst @@ -12,6 +12,8 @@ This article describes how data flow analysis is implemented in the CodeQL libra The following sections describe how to use the libraries for local data flow, global data flow, and taint tracking. For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `." +.. include:: ../reusables/new-data-flow-api.rst + Local data flow --------------- @@ -146,24 +148,24 @@ Global data flow tracks data flow throughout the entire program, and is therefor Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -The global data flow library is used by extending the class ``DataFlow::Configuration``: +The global data flow library is used by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global``: .. code-block:: ql import csharp - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + These predicates are defined in the configuration: - ``isSource`` - defines where data may flow from. @@ -171,45 +173,36 @@ These predicates are defined in the configuration: - ``isBarrier`` - optionally, restricts the data flow. - ``isAdditionalFlowStep`` - optionally, adds additional flow steps. -The characteristic predicate (``MyDataFlowConfiguration()``) defines the name of the configuration, so ``"..."`` must be replaced with a unique name. - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguation dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Dataflow to $@.", sink, sink.toString() Using global taint tracking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Global taint tracking is to global data flow what local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by extending the class ``TaintTracking::Configuration``: +Global taint tracking is to global data flow what local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global``: .. code-block:: ql import csharp - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -These predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource`` - defines where taint may flow from. -- ``isSink`` - defines where taint may flow to. -- ``isSanitizer`` - optionally, restricts the taint flow. -- ``isAdditionalTaintStep`` - optionally, adds additional taint steps. - -Similar to global data flow, the characteristic predicate (``MyTaintTrackingConfiguration()``) defines the unique name of the configuration and the taint analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Flow sources ~~~~~~~~~~~~ @@ -228,12 +221,8 @@ This query shows a data flow configuration that uses all public API parameters a import csharp import semmle.code.csharp.dataflow.flowsources.PublicCallableParameter - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { - this = "..." - } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof PublicCallableParameterFlowSource } @@ -243,7 +232,6 @@ This query shows a data flow configuration that uses all public API parameters a Class hierarchy ~~~~~~~~~~~~~~~ -- ``DataFlow::Configuration`` - base class for custom global data flow analysis. - ``DataFlow::Node`` - an element behaving as a data flow node. - ``DataFlow::ExprNode`` - an expression behaving as a data flow node. @@ -261,8 +249,6 @@ Class hierarchy - ``WcfRemoteFlowSource`` - data flow from a WCF web service. - ``AspNetServiceRemoteFlowSource`` - data flow from an ASP.NET web service. -- ``TaintTracking::Configuration`` - base class for custom global taint tracking analysis. - Examples ~~~~~~~~ @@ -272,17 +258,15 @@ This data flow configuration tracks data flow from environment variables to open import csharp - class EnvironmentToFileConfiguration extends DataFlow::Configuration { - EnvironmentToFileConfiguration() { this = "Environment opening files" } - - override predicate isSource(DataFlow::Node source) { + module EnvironmentToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Method m | m = source.asExpr().(MethodCall).getTarget() and m.hasQualifiedName("System.Environment.GetEnvironmentVariable") ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.IO.File.Open") and sink.asExpr() = mc.getArgument(0) @@ -290,8 +274,10 @@ This data flow configuration tracks data flow from environment variables to open } } - from Expr environment, Expr fileOpen, EnvironmentToFileConfiguration config - where config.hasFlow(DataFlow::exprNode(environment), DataFlow::exprNode(fileOpen)) + module EnvironmentToFileFlow = DataFlow::Global; + + from Expr environment, Expr fileOpen + where EnvironmentToFileFlow::flow(DataFlow::exprNode(environment), DataFlow::exprNode(fileOpen)) select fileOpen, "This 'File.Open' uses data from $@.", environment, "call to 'GetEnvironmentVariable'" @@ -435,21 +421,21 @@ Exercise 2 import csharp - class Configuration extends DataFlow::Configuration { - Configuration() { this="String to System.Uri" } - - override predicate isSource(DataFlow::Node src) { + module StringToUriConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr().hasValue() } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Call c | c.getTarget().(Constructor).getDeclaringType().hasQualifiedName("System.Uri") and sink.asExpr()=c.getArgument(0)) } } - from DataFlow::Node src, DataFlow::Node sink, Configuration config - where config.hasFlow(src, sink) + module StringToUriFlow = DataFlow::Global; + + from DataFlow::Node src, DataFlow::Node sink + where StringToUriFlow::flow(src, sink) select src, "This string constructs a 'System.Uri' $@.", sink, "here" Exercise 3 @@ -476,21 +462,21 @@ Exercise 4 } } - class Configuration extends DataFlow::Configuration { - Configuration() { this="Environment to System.Uri" } - - override predicate isSource(DataFlow::Node src) { + module EnvironmentToUriConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src instanceof EnvironmentVariableFlowSource } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Call c | c.getTarget().(Constructor).getDeclaringType().hasQualifiedName("System.Uri") and sink.asExpr()=c.getArgument(0)) } } - from DataFlow::Node src, DataFlow::Node sink, Configuration config - where config.hasFlow(src, sink) + module EnvironmentToUriFlow = DataFlow::Global; + + from DataFlow::Node src, DataFlow::Node sink + where EnvironmentToUriFlow::flow(src, sink) select src, "This environment variable constructs a 'System.Uri' $@.", sink, "here" Exercise 5 diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-java.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-java.rst index 2eccdf5e103..06ca7fe413f 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-java.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-java.rst @@ -17,6 +17,8 @@ The following sections describe how to use the libraries for local data flow, gl For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `." +.. include:: ../reusables/new-data-flow-api.rst + Local data flow --------------- @@ -160,24 +162,24 @@ Global data flow tracks data flow throughout the entire program, and is therefor Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -You use the global data flow library by extending the class ``DataFlow::Configuration``: +You use the global data flow library by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global``: .. code-block:: ql import semmle.code.java.dataflow.DataFlow - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + These predicates are defined in the configuration: - ``isSource``—defines where data may flow from @@ -185,47 +187,36 @@ These predicates are defined in the configuration: - ``isBarrier``—optional, restricts the data flow - ``isAdditionalFlowStep``—optional, adds additional flow steps -The characteristic predicate ``MyDataFlowConfiguration()`` defines the name of the configuration, so ``"MyDataFlowConfiguration"`` should be a unique name, for example, the name of your class. - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Data flow to $@.", sink, sink.toString() Using global taint tracking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. You use the global taint tracking library by extending the class ``TaintTracking::Configuration``: +Global taint tracking is to global data flow as local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. You use the global taint tracking library by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global``: .. code-block:: ql import semmle.code.java.dataflow.TaintTracking - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -These predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource``—defines where taint may flow from -- ``isSink``—defines where taint may flow to -- ``isSanitizer``—optional, restricts the taint flow -- ``isAdditionalTaintStep``—optional, adds additional taint steps - -Similar to global data flow, the characteristic predicate ``MyTaintTrackingConfiguration()`` defines the unique name of the configuration. - -The taint tracking analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Flow sources ~~~~~~~~~~~~ @@ -242,18 +233,16 @@ This query shows a taint-tracking configuration that uses remote user input as d import java import semmle.code.java.dataflow.FlowSources - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { - this = "..." - } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } ... } + module MyTaintFlow = TaintTracking::Global; + Exercises ~~~~~~~~~ @@ -287,16 +276,12 @@ Exercise 2 import semmle.code.java.dataflow.DataFlow - class Configuration extends DataFlow::Configuration { - Configuration() { - this = "LiteralToURL Configuration" - } - - override predicate isSource(DataFlow::Node source) { + module LiteralToURLConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof StringLiteral } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Call call | sink.asExpr() = call.getArgument(0) and call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL") @@ -304,8 +289,10 @@ Exercise 2 } } - from DataFlow::Node src, DataFlow::Node sink, Configuration config - where config.hasFlow(src, sink) + module LiteralToURLFlow = DataFlow::Global; + + from DataFlow::Node src, DataFlow::Node sink + where LiteralToURLFlow::flow(src, sink) select src, "This string constructs a URL $@.", sink, "here" Exercise 3 @@ -340,16 +327,12 @@ Exercise 4 } } - class GetenvToURLConfiguration extends DataFlow::Configuration { - GetenvToURLConfiguration() { - this = "GetenvToURLConfiguration" - } - - override predicate isSource(DataFlow::Node source) { + module GetenvToURLConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof GetenvSource } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Call call | sink.asExpr() = call.getArgument(0) and call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL") @@ -357,8 +340,10 @@ Exercise 4 } } - from DataFlow::Node src, DataFlow::Node sink, GetenvToURLConfiguration config - where config.hasFlow(src, sink) + module GetenvToURLFlow = DataFlow::Global; + + from DataFlow::Node src, DataFlow::Node sink + where GetenvToURLFlow::flow(src, sink) select src, "This environment variable constructs a URL $@.", sink, "here" Further reading @@ -368,4 +353,4 @@ Further reading .. include:: ../reusables/java-further-reading.rst -.. include:: ../reusables/codeql-ref-tools-further-reading.rst \ No newline at end of file +.. include:: ../reusables/codeql-ref-tools-further-reading.rst diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst index 2ad5fc925f0..e93f2df3b73 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-python.rst @@ -12,6 +12,8 @@ This article describes how data flow analysis is implemented in the CodeQL libra The following sections describe how to use the libraries for local data flow, global data flow, and taint tracking. For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `." +.. include:: ../reusables/new-data-flow-api.rst + Local data flow --------------- @@ -204,24 +206,24 @@ Global data flow tracks data flow throughout the entire program, and is therefor Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -The global data flow library is used by extending the class ``DataFlow::Configuration``: +The global data flow library is used by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global``: .. code-block:: ql import python - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + These predicates are defined in the configuration: - ``isSource`` - defines where data may flow from. @@ -229,45 +231,36 @@ These predicates are defined in the configuration: - ``isBarrier`` - optionally, restricts the data flow. - ``isAdditionalFlowStep`` - optionally, adds additional flow steps. -The characteristic predicate (``MyDataFlowConfiguration()``) defines the name of the configuration, so ``"..."`` must be replaced with a unique name (for instance the class name). - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguation dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Dataflow to $@.", sink, sink.toString() Using global taint tracking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Global taint tracking is to global data flow what local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by extending the class ``TaintTracking::Configuration``: +Global taint tracking is to global data flow what local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. The global taint tracking library is used by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global``: .. code-block:: ql import python - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -These predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource`` - defines where taint may flow from. -- ``isSink`` - defines where taint may flow to. -- ``isSanitizer`` - optionally, restricts the taint flow. -- ``isAdditionalTaintStep`` - optionally, adds additional taint steps. - -Similar to global data flow, the characteristic predicate (``MyTaintTrackingConfiguration()``) defines the unique name of the configuration and the taint analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Predefined sources and sinks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -283,7 +276,6 @@ For global flow, it is also useful to restrict sources to instances of ``LocalSo Class hierarchy ~~~~~~~~~~~~~~~ -- ``DataFlow::Configuration`` - base class for custom global data flow analysis. - ``DataFlow::Node`` - an element behaving as a data flow node. - ``DataFlow::CfgNode`` - a control-flow node behaving as a data flow node. @@ -305,8 +297,6 @@ Class hierarchy - ``Concepts::HTTP::Server::RouteSetup`` - a data-flow node that sets up a route on a server. - ``Concepts::HTTP::Server::HttpResponse`` - a data-flow node that creates a HTTP response on a server. -- ``TaintTracking::Configuration`` - base class for custom global taint tracking analysis. - Examples ~~~~~~~~ @@ -320,20 +310,20 @@ This query shows a data flow configuration that uses all network input as data s import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.Concepts - class RemoteToFileConfiguration extends TaintTracking::Configuration { - RemoteToFileConfiguration() { this = "RemoteToFileConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module RemoteToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fa).getAPathArgument() } } - from DataFlow::Node input, DataFlow::Node fileAccess, RemoteToFileConfiguration config - where config.hasFlow(input, fileAccess) + module RemoteToFileFlow = TaintTracking::Global; + + from DataFlow::Node input, DataFlow::Node fileAccess + where RemoteToFileFlow::flow(input, fileAccess) select fileAccess, "This file access uses data from $@.", input, "user-controllable input." @@ -345,14 +335,12 @@ This data flow configuration tracks data flow from environment variables to open import semmle.python.dataflow.new.TaintTracking import semmle.python.ApiGraphs - class EnvironmentToFileConfiguration extends DataFlow::Configuration { - EnvironmentToFileConfiguration() { this = "EnvironmentToFileConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module EnvironmentToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source = API::moduleImport("os").getMember("getenv").getACall() } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(DataFlow::CallCfgNode call | call = API::moduleImport("os").getMember("open").getACall() and sink = call.getArg(0) @@ -360,8 +348,10 @@ This data flow configuration tracks data flow from environment variables to open } } - from Expr environment, Expr fileOpen, EnvironmentToFileConfiguration config - where config.hasFlow(DataFlow::exprNode(environment), DataFlow::exprNode(fileOpen)) + module EnvironmentToFileFlow = DataFlow::Global; + + from Expr environment, Expr fileOpen + where EnvironmentToFileFlow::flow(DataFlow::exprNode(environment), DataFlow::exprNode(fileOpen)) select fileOpen, "This call to 'os.open' uses data from $@.", environment, "call to 'os.getenv'" diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst index b326bfa59aa..95744747cbc 100644 --- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst +++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst @@ -12,6 +12,8 @@ This article describes how data flow analysis is implemented in the CodeQL libra The following sections describe how to use the libraries for local data flow, global data flow, and taint tracking. For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `." +.. include:: ../reusables/new-data-flow-api.rst + Local data flow --------------- @@ -224,24 +226,24 @@ However, global data flow is less precise than local data flow, and the analysis Using global data flow ~~~~~~~~~~~~~~~~~~~~~~ -You can use the global data flow library by extending the class ``DataFlow::Configuration``: +You can use the global data flow library by implementing the signature ``DataFlow::ConfigSig`` and applying the module ``DataFlow::Global``: .. code-block:: ql import codeql.ruby.DataFlow - class MyDataFlowConfiguration extends DataFlow::Configuration { - MyDataFlowConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } + module MyFlow = DataFlow::Global; + These predicates are defined in the configuration: - ``isSource`` - defines where data may flow from. @@ -249,14 +251,12 @@ These predicates are defined in the configuration: - ``isBarrier`` - optionally, restricts the data flow. - ``isAdditionalFlowStep`` - optionally, adds additional flow steps. -The characteristic predicate (``MyDataFlowConfiguration()``) defines the name of the configuration, so ``"..."`` must be replaced with a unique name (for instance the class name). - -The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``: +The data flow analysis is performed using the predicate ``flow(DataFlow::Node source, DataFlow::Node sink)``: .. code-block:: ql - from MyDataFlowConfiguation dataflow, DataFlow::Node source, DataFlow::Node sink - where dataflow.hasFlow(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select source, "Dataflow to $@.", sink, sink.toString() Using global taint tracking @@ -264,33 +264,26 @@ Using global taint tracking Global taint tracking is to global data flow what local taint tracking is to local data flow. That is, global taint tracking extends global data flow with additional non-value-preserving steps. -The global taint tracking library is used by extending the class ``TaintTracking::Configuration``: +The global taint tracking library is used by applying the module ``TaintTracking::Global`` to your configuration instead of ``DataFlow::Global``: .. code-block:: ql import codeql.ruby.DataFlow import codeql.ruby.TaintTracking - class MyTaintTrackingConfiguration extends TaintTracking::Configuration { - MyTaintTrackingConfiguration() { this = "..." } - - override predicate isSource(DataFlow::Node source) { + module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { ... } } -These predicates are defined in the configuration: + module MyFlow = TaintTracking::Global; -- ``isSource`` - defines where taint may flow from. -- ``isSink`` - defines where taint may flow to. -- ``isSanitizer`` - optionally, restricts the taint flow. -- ``isAdditionalTaintStep`` - optionally, adds additional taint steps. - -Similar to global data flow, the characteristic predicate (``MyTaintTrackingConfiguration()``) defines the unique name of the configuration and the taint analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``. +The resulting module has an identical signature to the one obtained from ``DataFlow::Global``. Predefined sources and sinks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -306,7 +299,6 @@ The predefined sources generally do that. Class hierarchy ~~~~~~~~~~~~~~~ -- ``DataFlow::Configuration`` - base class for custom global data flow analysis. - ``DataFlow::Node`` - an element behaving as a data-flow node. - ``DataFlow::LocalSourceNode`` - a local origin of data, as a data-flow node. - ``DataFlow::ExprNode`` - an expression behaving as a data-flow node. @@ -321,13 +313,11 @@ Class hierarchy - ``Concepts::HTTP::Server::RouteSetup`` - a data-flow node that sets up a route on a server. - ``Concepts::HTTP::Server::HttpResponse`` - a data-flow node that creates an HTTP response on a server. -- ``TaintTracking::Configuration`` - base class for custom global taint tracking analysis. - Examples of global data flow ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following global taint-tracking query finds path arguments in filesystem accesses that can be controlled by a remote user. - - Since this is a taint-tracking query, the configuration class extends ``TaintTracking::Configuration``. + - Since this is a taint-tracking query, the ``TaintTracking::Global`` module is used. - The ``isSource`` predicate defines sources as any data-flow nodes that are instances of ``RemoteFlowSource``. - The ``isSink`` predicate defines sinks as path arguments in any filesystem access, using ``FileSystemAccess`` from the ``Concepts`` library. @@ -338,22 +328,22 @@ The following global taint-tracking query finds path arguments in filesystem acc import codeql.ruby.Concepts import codeql.ruby.dataflow.RemoteFlowSources - class RemoteToFileConfiguration extends TaintTracking::Configuration { - RemoteToFileConfiguration() { this = "RemoteToFileConfiguration" } + module RemoteToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fa).getAPathArgument() } } + + module RemoteToFileFlow = TaintTracking::Global; - from DataFlow::Node input, DataFlow::Node fileAccess, RemoteToFileConfiguration config - where config.hasFlow(input, fileAccess) + from DataFlow::Node input, DataFlow::Node fileAccess + where RemoteToFileFlow::flow(input, fileAccess) select fileAccess, "This file access uses data from $@.", input, "user-controllable input." The following global data-flow query finds calls to ``File.open`` where the filename argument comes from an environment variable. - - Since this is a data-flow query, the configuration class extends ``DataFlow::Configuration``. + - Since this is a data-flow query, the ``DataFlow::Global`` module is used. - The ``isSource`` predicate defines sources as expression nodes representing lookups on the ``ENV`` hash. - The ``isSink`` predicate defines sinks as the first argument in any call to ``File.open``. @@ -363,23 +353,23 @@ The following global data-flow query finds calls to ``File.open`` where the file import codeql.ruby.controlflow.CfgNodes import codeql.ruby.ApiGraphs - class EnvironmentToFileConfiguration extends DataFlow::Configuration { - EnvironmentToFileConfiguration() { this = "EnvironmentToFileConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module EnvironmentToFileConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(ExprNodes::ConstantReadAccessCfgNode env | env.getExpr().getName() = "ENV" and env = source.asExpr().(ExprNodes::ElementReferenceCfgNode).getReceiver() ) } - - override predicate isSink(DataFlow::Node sink) { + + predicate isSink(DataFlow::Node sink) { sink = API::getTopLevelMember("File").getAMethodCall("open").getArgument(0) } } + + module EnvironmentToFileFlow = DataFlow::Global; - from EnvironmentToFileConfiguration config, DataFlow::Node environment, DataFlow::Node fileOpen - where config.hasFlow(environment, fileOpen) + from DataFlow::Node environment, DataFlow::Node fileOpen + where EnvironmentToFileFlow::flow(environment, fileOpen) select fileOpen, "This call to 'File.open' uses data from $@.", environment, "an environment variable" diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-go.rst b/docs/codeql/codeql-language-guides/codeql-library-for-go.rst index f1f77c5150e..33468ab7a71 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-go.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-go.rst @@ -494,14 +494,14 @@ which are sets of data-flow nodes. Given these three sets, CodeQL provides a gen finding paths from a source to a sink, possibly going into and out of functions and fields, but never flowing through a barrier. -To define a data-flow configuration, you can define a subclass of ``DataFlow::Configuration``, -overriding the member predicates ``isSource``, ``isSink``, and ``isBarrier`` to define the sets of -sources, sinks, and barriers. +To define a data-flow configuration, you can define a module implementing ``DataFlow::ConfigSig``, +including the predicates ``isSource``, ``isSink``, and ``isBarrier`` to define the sets of +sources, sinks, and barriers. Data flow is then computed by applying +``DataFlow::Global<..>`` to the configuration. Going beyond pure data flow, many security analyses need to perform more general `taint tracking`, which also considers flow through value-transforming operations such as string operations. To track -taint, you can define a subclass of ``TaintTracking::Configuration``, which works similar to -data-flow configurations. +taint, you apply ``TaintTracking::Global<..>`` to your configuration instead. A detailed exposition of global data flow and taint tracking is out of scope for this brief introduction. For a general overview of data flow and taint tracking, see ":ref:`About data flow analysis `." diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst index d5cf4e0338e..99f6edd055c 100644 --- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst @@ -220,6 +220,59 @@ For example, the **mysql** model that is included with the CodeQL JS analysis in - ["mysql.Connection", "mysql", "Member[createConnection].ReturnValue"] +Example: Using fuzzy models to simplify modeling +------------------------------------------------ + +In this example, we'll show how to add the following SQL injection sink using a "fuzzy" model: + +.. code-block:: ts + + import * as mysql from 'mysql'; + const pool = mysql.createPool({...}); + pool.getConnection((err, conn) => { + conn.query(q, (err, rows) => {...}); // <-- add 'q' as a SQL injection sink + }); + +We can recognize this using a fuzzy model, as shown in the following extension: + +.. code-block:: yaml + + extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["mysql", "Fuzzy.Member[query].Argument[0]", "sql-injection"] + +- The first column, **"mysql"**, begins the search at places where the `mysql` package is imported. +- **Fuzzy** selects all objects that appear to originate from the `mysql` package, such as the `pool`, `conn`, `err`, and `rows` objects. +- **Member[query]** selects the **query** member from any of those objects. In this case, the only such member is `conn.query`. + In principle, this would also find expressions such as `pool.query` and `err.query`, but in practice such expressions + are not likely to occur, because the `pool` and `err` objects do not have a member named `query`. +- **Argument[0]** selects the first argument of a call to the selected member, that is, the `q` argument to `conn.query`. +- **sql-injection** indicates that this is considered as a sink for the SQL injection query. + +For reference, a more detailed model might look like this, as described in the preceding examples: + +.. code-block:: yaml + + extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["mysql.Connection", "Member[query].Argument[0]", "sql-injection"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["mysql.Pool", "mysql", "Member[createPool].ReturnValue"] + - ["mysql.Connection", "mysql.Pool", "Member[getConnection].Argument[0].Parameter[1]"] + +The model using the **Fuzzy** component is simpler, at the cost of being approximate. +This technique is useful when modeling a large or complex library, where it is difficult to write a detailed model. + Example: Adding flow through 'decodeURIComponent' ------------------------------------------------- @@ -431,6 +484,9 @@ The following components are supported: - **MapValue** selects a value of a map object. - **Awaited** selects the value of a promise. - **Instance** selects instances of a class. +- **Fuzzy** selects all values that are derived from the current value through a combination of the other operations described in this list. + For example, this can be used to find all values that appear to originate from a particular package. This can be useful for finding method calls + from a known package, but where the receiver type is not known or is difficult to model. The following components are called "call site filters". They select a subset of the previously-selected calls, if the call fits certain criteria: @@ -471,6 +527,7 @@ Unlike sources, sinks tend to be highly query-specific, rarely affecting more th - **request-forgery**: A sink that controls the URL of a request, such as in a **fetch** call. - **url-redirection**: A sink that can be used to redirect the user to a malicious URL. - **unsafe-deserialization**: A deserialization sink that can lead to code execution or other unsafe behaviour, such as an unsafe YAML parser. +- **log-injection**: A sink that can be used for log injection, such as in a **console.log** call. Summary kinds ~~~~~~~~~~~~~ diff --git a/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst b/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst index 7ac699b61c2..58a16b4a464 100644 --- a/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst +++ b/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst @@ -161,20 +161,20 @@ is read flows into a call to ``File.write``. import codeql.ruby.DataFlow import codeql.ruby.ApiGraphs - class Configuration extends DataFlow::Configuration { - Configuration() { this = "File read/write Configuration" } - - override predicate isSource(DataFlow::Node source) { + module Configuration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source = API::getTopLevelMember("File").getMethod("read").getReturn().asSource() } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink = API::getTopLevelMember("File").getMethod("write").getParameter(1).asSink() } } - from DataFlow::Node src, DataFlow::Node sink, Configuration config - where config.hasFlow(src, sink) + module Flow = DataFlow::Global; + + from DataFlow::Node src, DataFlow::Node sink + where Flow::flow(src, sink) select src, "The data read here flows into a $@ call.", sink, "File.write" Further reading diff --git a/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-result.png b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-result.png index da41f4c8219..45410abcee7 100644 Binary files a/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-result.png and b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-result.png differ diff --git a/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-results-view.png b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-results-view.png index d9998fdfdfd..0d1fc07c849 100644 Binary files a/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-results-view.png and b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-results-view.png differ diff --git a/docs/codeql/ql-training/cpp/global-data-flow-cpp.rst b/docs/codeql/ql-training/cpp/global-data-flow-cpp.rst index 5fdac594389..a2391e40332 100644 --- a/docs/codeql/ql-training/cpp/global-data-flow-cpp.rst +++ b/docs/codeql/ql-training/cpp/global-data-flow-cpp.rst @@ -47,8 +47,8 @@ The library class ``SecurityOptions`` provides a (configurable) model of what co import semmle.code.cpp.security.Security - class TaintedFormatConfig extends TaintTracking::Configuration { - override predicate isSource(DataFlow::Node source) { + module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists (SecurityOptions opts | opts.isUserInput(source.asExpr(), _) ) @@ -70,8 +70,8 @@ Use the ``FormattingFunction`` class to fill in the definition of ``isSink``. import semmle.code.cpp.security.Security - class TaintedFormatConfig extends TaintTracking::Configuration { - override predicate isSink(DataFlow::Node sink) { + module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isSink(DataFlow::Node sink) { /* Fill me in */ } ... @@ -90,8 +90,8 @@ Use the ``FormattingFunction`` class, we can write the sink as: import semmle.code.cpp.security.Security - class TaintedFormatConfig extends TaintTracking::Configuration { - override predicate isSink(DataFlow::Node sink) { + module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isSink(DataFlow::Node sink) { exists (FormattingFunction ff, Call c | c.getTarget() = ff and c.getArgument(ff.getFormatParameterIndex()) = sink.asExpr() @@ -117,9 +117,8 @@ Add an additional taint step that (heuristically) taints a local variable if it .. code-block:: ql - class TaintedFormatConfig extends TaintTracking::Configuration { - override predicate isAdditionalTaintStep(DataFlow::Node pred, - DataFlow::Node succ) { + module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { exists (Call c, Expr arg, LocalVariable lv | arg = c.getAnArgument() and arg = pred.asExpr() and @@ -138,8 +137,8 @@ Add a sanitizer, stopping propagation at parameters of formatting functions, to .. code-block:: ql - class TaintedFormatConfig extends TaintTracking::Configuration { - override predicate isSanitizer(DataFlow::Node nd) { + module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isBarrier(DataFlow::Node nd) { exists (FormattingFunction ff, int idx | idx = ff.getFormatParameterIndex() and nd = DataFlow::parameterNode(ff.getParameter(idx)) diff --git a/docs/codeql/ql-training/java/apache-struts-java.rst b/docs/codeql/ql-training/java/apache-struts-java.rst index 133c64a5851..24186bda48f 100644 --- a/docs/codeql/ql-training/java/apache-struts-java.rst +++ b/docs/codeql/ql-training/java/apache-struts-java.rst @@ -56,7 +56,7 @@ Finding the RCE yourself **Hint**: Use ``Method.getDeclaringType()`` and ``Type.getASupertype()`` -#. Implement a ``DataFlow::Configuration``, defining the source as the first parameter of a ``toObject`` method, and the sink as an instance of ``UnsafeDeserializationSink``. +#. Implement a ``DataFlow::ConfigSig``, defining the source as the first parameter of a ``toObject`` method, and the sink as an instance of ``UnsafeDeserializationSink``. **Hint**: Use ``Node::asParameter()`` @@ -99,13 +99,13 @@ Model answer, step 3 * Configuration that tracks the flow of taint from the first parameter of * `ContentTypeHandler.toObject` to an instance of unsafe deserialization. */ - class StrutsUnsafeDeserializationConfig extends Configuration { - StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" } - override predicate isSource(Node source) { + module StrutsUnsafeDeserializationConfig implements ConfigSig { + predicate isSource(Node source) { source.asParameter() = any(ContentTypeHandlerDeserialization des).getParameter(0) } - override predicate isSink(Node sink) { sink instanceof UnsafeDeserializationSink } + predicate isSink(Node sink) { sink instanceof UnsafeDeserializationSink } } + module StrutsUnsafeDeserializationFlow = Global; Model answer, step 4 ==================== @@ -114,9 +114,8 @@ Model answer, step 4 import PathGraph ... - from PathNode source, PathNode sink, StrutsUnsafeDeserializationConfig conf - where conf.hasFlowPath(source, sink) - and sink.getNode() instanceof UnsafeDeserializationSink - select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input" + from PathNode source, PathNode sink + where StrutsUnsafeDeserializationFlow::flowPath(source, sink) + select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input" More full-featured version: https://github.com/github/securitylab/tree/main/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805 diff --git a/docs/codeql/ql-training/java/global-data-flow-java.rst b/docs/codeql/ql-training/java/global-data-flow-java.rst index 940411d41a3..ddee9645d17 100644 --- a/docs/codeql/ql-training/java/global-data-flow-java.rst +++ b/docs/codeql/ql-training/java/global-data-flow-java.rst @@ -63,12 +63,12 @@ We want to look for method calls where the method name is ``getNamespace()``, an import semmle.code.java.security.Security - class TaintedOGNLConfig extends TaintTracking::Configuration { - override predicate isSource(DataFlow::Node source) { + module TaintedOGNLConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Method m | - m.getName() = "getNamespace" and - m.getDeclaringType().getName() = "ActionProxy" and - source.asExpr() = m.getAReference() + m.getName() = "getNamespace" and + m.getDeclaringType().getName() = "ActionProxy" and + source.asExpr() = m.getAReference() ) } ... @@ -90,8 +90,8 @@ Fill in the definition of ``isSink``. import semmle.code.java.security.Security - class TaintedOGNLConfig extends TaintTracking::Configuration { - override predicate isSink(DataFlow::Node sink) { + module TaintedOGNLConfig implements DataFlow::ConfigSig { + predicate isSink(DataFlow::Node sink) { /* Fill me in */ } ... @@ -110,9 +110,9 @@ Find a method access to ``compileAndExecute``, and mark the first argument. import semmle.code.java.security.Security - class TaintedOGNLConfig extends TaintTracking::Configuration { - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | + module TaintedOGNLConfig implements DataFlow::ConfigSig { + predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | ma.getMethod().getName() = "compileAndExecute" and ma.getArgument(0) = sink.asExpr() ) @@ -133,8 +133,8 @@ A sanitizer allows us to *prevent* flow through a particular node in the graph. .. code-block:: ql - class TaintedOGNLConfig extends TaintTracking::Configuration { - override predicate isSanitizer(DataFlow::Node nd) { + module TaintedOGNLConfig implements DataFlow::ConfigSig { + predicate isBarrier(DataFlow::Node nd) { nd.getEnclosingCallable() .getDeclaringType() .getName() = "ValueStackShadowMap" @@ -149,9 +149,8 @@ Add an additional taint step that (heuristically) taints a local variable if it .. code-block:: ql - class TaintedOGNLConfig extends TaintTracking::Configuration { - override predicate isAdditionalTaintStep(DataFlow::Node node1, - DataFlow::Node node2) { + module TaintedOGNLConfig implements DataFlow::ConfigSig { + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { exists(Field f, RefType t | node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess() and diff --git a/docs/codeql/ql-training/query-examples/cpp/global-data-flow-cpp-1.ql b/docs/codeql/ql-training/query-examples/cpp/global-data-flow-cpp-1.ql index 9020da5ea2c..3d203c0e147 100644 --- a/docs/codeql/ql-training/query-examples/cpp/global-data-flow-cpp-1.ql +++ b/docs/codeql/ql-training/query-examples/cpp/global-data-flow-cpp-1.ql @@ -1,14 +1,14 @@ import cpp import semmle.code.cpp.dataflow.TaintTracking -class TaintedFormatConfig extends TaintTracking::Configuration { - TaintedFormatConfig() { this = "TaintedFormatConfig" } +module TaintedFormatConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { /* TBD */ } - override predicate isSource(DataFlow::Node source) { /* TBD */ } - - override predicate isSink(DataFlow::Node sink) { /* TBD */ } + predicate isSink(DataFlow::Node sink) { /* TBD */ } } -from TaintedFormatConfig cfg, DataFlow::Node source, DataFlow::Node sink -where cfg.hasFlow(source, sink) +module TaintedFormatFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where TaintedFormatFlow::flow(source, sink) select sink, "This format string may be derived from a $@.", source, "user-controlled value" diff --git a/docs/codeql/ql-training/query-examples/java/global-data-flow-java-1.ql b/docs/codeql/ql-training/query-examples/java/global-data-flow-java-1.ql index 7604b20eb95..431787cac45 100644 --- a/docs/codeql/ql-training/query-examples/java/global-data-flow-java-1.ql +++ b/docs/codeql/ql-training/query-examples/java/global-data-flow-java-1.ql @@ -1,14 +1,14 @@ import java import semmle.code.java.dataflow.TaintTracking -class TaintedOGNLConfig extends TaintTracking::Configuration { - TaintedOGNLConfig() { this = "TaintedOGNLConfig" } +module TaintedOgnlConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { /* TBD */ } - override predicate isSource(DataFlow::Node source) { /* TBD */ } - - override predicate isSink(DataFlow::Node sink) { /* TBD */ } + predicate isSink(DataFlow::Node sink) { /* TBD */ } } -from TaintedOGNLConfig cfg, DataFlow::Node source, DataFlow::Node sink -where cfg.hasFlow(source, sink) +module TaintedOgnlFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where TaintedOgnlFlow::flow(source, sink) select source, "This untrusted input is evaluated as an OGNL expression $@.", sink, "here" diff --git a/docs/codeql/ql-training/slide-snippets/global-data-flow.rst b/docs/codeql/ql-training/slide-snippets/global-data-flow.rst index 6cb52af0f84..f84db924e27 100644 --- a/docs/codeql/ql-training/slide-snippets/global-data-flow.rst +++ b/docs/codeql/ql-training/slide-snippets/global-data-flow.rst @@ -34,18 +34,18 @@ Global taint tracking library The ``semmle.code..dataflow.TaintTracking`` library provides a framework for implementing solvers for global taint tracking problems: - #. Subclass ``TaintTracking::Configuration`` following this template: + #. Implement ``DataFlow::ConfigSig`` and use ``TaintTracking::Global`` following this template: .. code-block:: ql - class Config extends TaintTracking::Configuration { - Config() { this = "" } - override predicate isSource(DataFlow::Node nd) { ... } - override predicate isSink(DataFlow::Node nd) { ... } + module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node nd) { ... } + predicate isSink(DataFlow::Node nd) { ... } } + module Flow = TaintTracking::Global; - #. Use ``Config.hasFlow(source, sink)`` to find inter-procedural paths. + #. Use ``Flow::flow(source, sink)`` to find inter-procedural paths. .. note:: - In addition to the taint tracking configuration described here, there is also an equivalent *data flow* configuration in ``semmle.code..dataflow.DataFlow``, ``DataFlow::Configuration``. Data flow configurations are used to track whether the exact value produced by a source is used by a sink, whereas taint tracking configurations are used to determine whether the source may influence the value used at the sink. Whether you use taint tracking or data flow depends on the analysis problem you are trying to solve. + In addition to the taint tracking flow configuration described here, there is also an equivalent *data flow* in ``semmle.code..dataflow.DataFlow``, ``DataFlow::Global``. Data flow is used to track whether the exact value produced by a source is used by a sink, whereas taint tracking is used to determine whether the source may influence the value used at the sink. Whether you use taint tracking or data flow depends on the analysis problem you are trying to solve. diff --git a/docs/codeql/ql-training/slide-snippets/path-queries.rst b/docs/codeql/ql-training/slide-snippets/path-queries.rst index e93f7fda966..6ed8b79814d 100644 --- a/docs/codeql/ql-training/slide-snippets/path-queries.rst +++ b/docs/codeql/ql-training/slide-snippets/path-queries.rst @@ -13,12 +13,16 @@ Use this template: */ import semmle.code..dataflow.TaintTracking - import DataFlow::PathGraph + ... - from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink - where cfg.hasFlowPath(source, sink) + + module Flow = TaintTracking::Global; + import Flow::PathGraph + + from Flow::PathNode source, Flow::PathNode sink + where Flow::flowPath(source, sink) select sink, source, sink, "" .. note:: - To see the paths between the source and the sinks, we can convert the query to a path problem query. There are a few minor changes that need to be made for this to work–we need an additional import, to specify ``PathNode`` rather than ``Node``, and to add the source/sink to the query output (so that we can automatically determine the paths). \ No newline at end of file + To see the paths between the source and the sinks, we can convert the query to a path problem query. There are a few minor changes that need to be made for this to work–we need an additional import, to specify ``PathNode`` rather than ``Node``, and to add the source/sink to the query output (so that we can automatically determine the paths). diff --git a/docs/codeql/reusables/new-data-flow-api.rst b/docs/codeql/reusables/new-data-flow-api.rst new file mode 100644 index 00000000000..dab3cca5c4f --- /dev/null +++ b/docs/codeql/reusables/new-data-flow-api.rst @@ -0,0 +1,4 @@ +.. pull-quote:: Note + + The new modular API for data flow described here is available alongside the previous library from CodeQL 2.13.0 onwards. For information about how the library has changed and how to migrate any existing queries to the modular API, see `New dataflow API for CodeQL query writing `__. + \ No newline at end of file diff --git a/docs/codeql/reusables/supported-frameworks.rst b/docs/codeql/reusables/supported-frameworks.rst index 520969d51c8..068bd27207f 100644 --- a/docs/codeql/reusables/supported-frameworks.rst +++ b/docs/codeql/reusables/supported-frameworks.rst @@ -121,6 +121,7 @@ and the CodeQL library pack ``codeql/java-all`` (`changelog // For some languages (Java/C++/Python/Swift) you need to explicitly import the data flow library, such as // import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow - import DataFlow::PathGraph ... - from MyConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink - where config.hasFlowPath(source, sink) + module Flow = DataFlow::Global; + import Flow::PathGraph + + from Flow::PathNode source, Flow::PathNode sink + where Flow::flowPath(source, sink) select sink.getNode(), source, sink, "" Where: -- ``DataFlow::Pathgraph`` is the path graph module you need to import from the standard CodeQL libraries. -- ``source`` and ``sink`` are nodes on the `path graph `__, and ``DataFlow::PathNode`` is their type. -- ``MyConfiguration`` is a class containing the predicates which define how data may flow between the ``source`` and the ``sink``. +- ``MyConfiguration`` is a module containing the predicates that define how data may flow between the ``source`` and the ``sink``. +- ``Flow`` is the result of the data flow computation based on ``MyConfiguration``. +- ``Flow::Pathgraph`` is the resulting data flow graph module you need to import in order to include path explanations in the query. +- ``source`` and ``sink`` are nodes in the graph as defined in the configuration, and ``Flow::PathNode`` is their type. +- ``DataFlow::Global<..>`` is an invocation of data flow. ``TaintTracking::Global<..>`` can be used instead to include a default set of additional taint steps. The following sections describe the main requirements for a valid path query. @@ -83,14 +89,14 @@ The other metadata requirements depend on how you intend to run the query. For m Generating path explanations **************************** -In order to generate path explanations, your query needs to compute a `path graph `__. +In order to generate path explanations, your query needs to compute a graph. To do this you need to define a :ref:`query predicate ` called ``edges`` in your query. This predicate defines the edge relations of the graph you are computing, and it is used to compute the paths related to each result that your query generates. You can import a predefined ``edges`` predicate from a path graph module in one of the standard data flow libraries. In addition to the path graph module, the data flow libraries contain the other ``classes``, ``predicates``, and ``modules`` that are commonly used in data flow analysis. .. code-block:: ql - import DataFlow::PathGraph + import MyFlow::PathGraph This statement imports the ``PathGraph`` module from the data flow library (``DataFlow.qll``), in which ``edges`` is defined. @@ -106,7 +112,7 @@ You can also define your own ``edges`` predicate in the body of your query. It s .. code-block:: ql query predicate edges(PathNode a, PathNode b) { - /** Logical conditions which hold if `(a,b)` is an edge in the data flow graph */ + /* Logical conditions which hold if `(a,b)` is an edge in the data flow graph */ } For more examples of how to define an ``edges`` predicate, visit the `standard CodeQL libraries `__ and search for ``edges``. @@ -117,14 +123,23 @@ Declaring sources and sinks You must provide information about the ``source`` and ``sink`` in your path query. These are objects that correspond to the nodes of the paths that you are exploring. The name and the type of the ``source`` and the ``sink`` must be declared in the ``from`` statement of the query, and the types must be compatible with the nodes of the graph computed by the ``edges`` predicate. -If you are querying C/C++, C#, Go, Java, JavaScript, Python, or Ruby code (and you have used ``import DataFlow::PathGraph`` in your query), the definitions of the ``source`` and ``sink`` are accessed via the ``Configuration`` class in the data flow library. You should declare all three of these objects in the ``from`` statement. +If you are querying C/C++, C#, Go, Java, JavaScript, Python, or Ruby code (and you have used ``import MyFlow::PathGraph`` in your query), the definitions of the ``source`` and ``sink`` are accessed via the module resulting from the application of the ``Global<..>`` module in the data flow library. You should declare both of these objects in the ``from`` statement. For example: .. code-block:: ql - from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink + module MyFlow = DataFlow::Global; -The configuration class is accessed by importing the data flow library. This class contains the predicates which define how data flow is treated in the query: + from MyFlow::PathNode source, MyFlow::PathNode sink + +The configuration module must be defined to include definitions of sources and sinks. For example: + +.. code-block:: ql + + module MyConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } + predicate isSink(DataFlow::Node source) { ... } + } - ``isSource()`` defines where data may flow from. - ``isSink()`` defines where data may flow to. @@ -141,11 +156,11 @@ This clause can use :ref:`aggregations `, :ref:`predicates ` to compute the possible values that a variable can hold at various points in a program. @@ -11,24 +13,24 @@ A typical data-flow query looks like this: .. code-block:: ql - class MyConfig extends TaintTracking::Configuration { - MyConfig() { this = "MyConfig" } + module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof MySource } - override predicate isSource(DataFlow::Node node) { node instanceof MySource } - - override predicate isSink(DataFlow::Node node) { node instanceof MySink } + predicate isSink(DataFlow::Node node) { node instanceof MySink } } - from MyConfig config, DataFlow::PathNode source, DataFlow::PathNode sink - where config.hasFlowPath(source, sink) + module MyFlow = TaintTracking::Global; + + from MyFlow::PathNode source, MyFlow::PathNode sink + where MyFlow::flowPath(source, sink) select sink.getNode(), source, sink, "Sink is reached from $@.", source.getNode(), "here" The same query can be slightly simplified by rewriting it without :ref:`path explanations `: .. code-block:: ql - from MyConfig config, DataFlow::Node source, DataFlow::Node sink - where config.hasPath(source, sink) + from DataFlow::Node source, DataFlow::Node sink + where MyFlow::flow(source, sink) select sink, "Sink is reached from $@.", source.getNode(), "here" If a data-flow query that you have written doesn't produce the results you expect it to, there may be a problem with your query. @@ -48,7 +50,7 @@ Data-flow configurations contain a parameter called ``fieldFlowBranchLimit``. If .. code-block:: ql - override int fieldFlowBranchLimit() { result = 5000 } + int fieldFlowBranchLimit() { result = 5000 } If there are still no results and performance is still useable, then it is best to leave this set to a high value while doing further debugging. @@ -57,7 +59,7 @@ Partial flow A naive next step could be to change the sink definition to ``any()``. This would mean that we would get a lot of flow to all the places that are reachable from the sources. While this approach may work in some cases, you might find that it produces so many results that it's very hard to explore the findings. It can also dramatically affect query performance. More importantly, you might not even see all the partial flow paths. This is because the data-flow library tries very hard to prune impossible paths and, since field stores and reads must be evenly matched along a path, we will never see paths going through a store that fail to reach a corresponding read. This can make it hard to see where flow actually stops. -To avoid these problems, a data-flow ``Configuration`` comes with a mechanism for exploring partial flow that tries to deal with these caveats. This is the ``Configuration.hasPartialFlow`` predicate: +To avoid these problems, the data-flow library comes with a mechanism for exploring partial flow that tries to deal with these caveats. This is the ``MyFlow::FlowExploration::partialFlow`` predicate: .. code-block:: ql @@ -71,25 +73,23 @@ To avoid these problems, a data-flow ``Configuration`` comes with a mechanism fo * perform poorly if the number of sources is too big and/or the exploration * limit is set too high without using barriers. * - * This predicate is disabled (has no results) by default. Override - * `explorationLimit()` with a suitable number to enable this predicate. - * * To use this in a `path-problem` query, import the module `PartialPathGraph`. */ - final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { -There is also a ``Configuration.hasPartialFlowRev`` for exploring flow backwards from a sink. +There is also a ``partialFlowRev`` for exploring flow backwards from a sink. -As noted in the documentation for ``hasPartialFlow`` (for example, in the -`CodeQL for Java documentation `__) you must first enable this by adding an override of ``explorationLimit``. For example: +To get access to these predicates you must instantiate the ``MyFlow::FlowExploration<>`` module with an exploration limit. For example: .. code-block:: ql - override int explorationLimit() { result = 5 } + int explorationLimit() { result = 5 } -This defines the exploration radius within which ``hasPartialFlow`` returns results. + module MyPartialFlow = MyFlow::FlowExploration; -To get good performance when using ``hasPartialFlow`` it is important to ensure the ``isSink`` predicate of the configuration has no results. Likewise, when using ``hasPartialFlowRev`` the ``isSource`` predicate of the configuration should have no results. +This defines the exploration radius within which ``partialFlow`` returns results. + +To get good performance when using ``partialFlow`` it is important to ensure the ``isSink`` predicate of the configuration has no results. Likewise, when using ``partialFlowRev`` the ``isSource`` predicate of the configuration should have no results. It is also useful to focus on a single source at a time as the starting point for the flow exploration. This is most easily done by adding a temporary restriction in the ``isSource`` predicate. @@ -97,9 +97,9 @@ To do quick evaluations of partial flow it is often easiest to add a predicate t .. code-block:: ql - predicate adhocPartialFlow(Callable c, PartialPathNode n, Node src, int dist) { - exists(MyConfig conf, PartialPathNode source | - conf.hasPartialFlow(source, n, dist) and + predicate adhocPartialFlow(Callable c, MyPartialFlow::PartialPathNode n, Node src, int dist) { + exists(MyPartialFlow::PartialPathNode source | + MyPartialFlow::partialFlow(source, n, dist) and src = source.getNode() and c = n.getNode().getEnclosingCallable() ) @@ -111,7 +111,7 @@ If you are focusing on a single source then the ``src`` column is superfluous. Y If you see a large number of partial flow results, you can focus them in a couple of ways: - If flow travels a long distance following an expected path, that can result in a lot of uninteresting flow being included in the exploration radius. To reduce the amount of uninteresting flow, you can replace the source definition with a suitable ``node`` that appears along the path and restart the partial flow exploration from that point. -- Creative use of barriers and sanitizers can be used to cut off flow paths that are uninteresting. This also reduces the number of partial flow results to explore while debugging. +- Creative use of barriers can be used to cut off flow paths that are uninteresting. This also reduces the number of partial flow results to explore while debugging. Further reading ---------------- diff --git a/docs/codeql/writing-codeql-queries/find-the-thief.rst b/docs/codeql/writing-codeql-queries/find-the-thief.rst index 5f0fcb9c015..dfb2e1998ba 100644 --- a/docs/codeql/writing-codeql-queries/find-the-thief.rst +++ b/docs/codeql/writing-codeql-queries/find-the-thief.rst @@ -307,14 +307,14 @@ Exercise 1 from Person t where - /* 1 */ t.getHeight() > 150 and - /* 2 */ not t.getHairColor() = "blond" and - /* 3 */ exists (string c | t.getHairColor() = c) and - /* 4 */ not t.getAge() < 30 and - /* 5 */ t.getLocation() = "east" and - /* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and - /* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and - /* 8 */ exists(Person p | p.getAge() > t.getAge()) + /* 1 */ t.getHeight() > 150 and + /* 2 */ not t.getHairColor() = "blond" and + /* 3 */ exists (string c | t.getHairColor() = c) and + /* 4 */ not t.getAge() < 30 and + /* 5 */ t.getLocation() = "east" and + /* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and + /* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and + /* 8 */ exists(Person p | p.getAge() > t.getAge()) select t Exercise 2 @@ -326,16 +326,16 @@ Exercise 2 from Person t where - /* 1 */ t.getHeight() > 150 and - /* 2 */ not t.getHairColor() = "blond" and - /* 3 */ exists (string c | t.getHairColor() = c) and - /* 4 */ not t.getAge() < 30 and - /* 5 */ t.getLocation() = "east" and - /* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and - /* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and - /* 8 */ exists(Person p | p.getAge() > t.getAge()) and - /* 9 */ not t = max(Person p | | p order by p.getHeight()) and - /* 10 */ t.getHeight() < avg(float i | exists(Person p | p.getHeight() = i) | i) and - /* 11 */ t = max(Person p | p.getLocation() = "east" | p order by p.getAge()) + /* 1 */ t.getHeight() > 150 and + /* 2 */ not t.getHairColor() = "blond" and + /* 3 */ exists (string c | t.getHairColor() = c) and + /* 4 */ not t.getAge() < 30 and + /* 5 */ t.getLocation() = "east" and + /* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and + /* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and + /* 8 */ exists(Person p | p.getAge() > t.getAge()) and + /* 9 */ not t = max(Person p | | p order by p.getHeight()) and + /* 10 */ t.getHeight() < avg(float i | exists(Person p | p.getHeight() = i) | i) and + /* 11 */ t = max(Person p | p.getLocation() = "east" | p order by p.getAge()) select "The thief is " + t + "!" \ No newline at end of file diff --git a/docs/ql-design-patterns.md b/docs/ql-design-patterns.md index b6d7eab88fc..8d8d126cba2 100644 --- a/docs/ql-design-patterns.md +++ b/docs/ql-design-patterns.md @@ -72,14 +72,12 @@ Importing new files can modify the behaviour of the standard library, by introdu Therefore, unless you have good reason not to, you should ensure that all subclasses are included when the base-class is (to the extent possible). -One example where this _does not_ apply: `DataFlow::Configuration` and its variants are meant to be subclassed, but we generally do not want to import all configurations into the same scope at once. - ## Abstract classes as open or closed unions A class declared as `abstract` in QL represents a union of its direct subtypes (restricted by the intersections of its supertypes and subject to its characteristic predicate). Depending on context, we may want this union to be considered "open" or "closed". -An open union is generally used for extensibility. For example, the abstract classes suggested by the `::Range` design pattern are explicitly intended as extension hooks. As another example, the `DataFlow::Configuration` design pattern provides an abstract class that is intended to be subclassed as a configuration mechanism. +An open union is generally used for extensibility. For example, the abstract classes suggested by the `::Range` design pattern are explicitly intended as extension hooks. A closed union is a class for which we do not expect users of the library to add more values. Historically, we have occasionally modelled this as `abstract` classes in QL, but these days that would be considered an anti-pattern: Abstract classes that are intended to be closed behave in surprising ways when subclassed by library users, and importing libraries that include derived classes can invalidate compilation caches and subvert the meaning of the program. diff --git a/go/Makefile b/go/Makefile index 8f28079f008..4cacb3bfc21 100644 --- a/go/Makefile +++ b/go/Makefile @@ -14,7 +14,7 @@ CODEQL_PLATFORM = osx64 endif endif -CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh pre-finalize.cmd pre-finalize.sh index.cmd index.sh identify-environment.cmd identify-environment.sh tracing-config.lua) +CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh baseline-config-empty.json baseline-config-vendor.json configure-baseline.cmd configure-baseline.sh identify-environment.cmd identify-environment.sh index.cmd index.sh pre-finalize.cmd pre-finalize.sh tracing-config.lua) EXTRACTOR_PACK_OUT = build/codeql-extractor-go diff --git a/go/codeql-tools/baseline-config-empty.json b/go/codeql-tools/baseline-config-empty.json new file mode 100644 index 00000000000..568d688fc3f --- /dev/null +++ b/go/codeql-tools/baseline-config-empty.json @@ -0,0 +1,3 @@ +{ + "paths-ignore": [] +} \ No newline at end of file diff --git a/go/codeql-tools/baseline-config-vendor.json b/go/codeql-tools/baseline-config-vendor.json new file mode 100644 index 00000000000..d2f654073b0 --- /dev/null +++ b/go/codeql-tools/baseline-config-vendor.json @@ -0,0 +1,5 @@ +{ + "paths-ignore": [ + "vendor/**" + ] +} \ No newline at end of file diff --git a/go/codeql-tools/configure-baseline.cmd b/go/codeql-tools/configure-baseline.cmd new file mode 100644 index 00000000000..7812c14a102 --- /dev/null +++ b/go/codeql-tools/configure-baseline.cmd @@ -0,0 +1,6 @@ +@echo off +if exist vendor\modules.txt ( + type %CODEQL_EXTRACTOR_GO_ROOT%\tools\baseline-config-vendor.json +) else ( + type %CODEQL_EXTRACTOR_GO_ROOT%\tools\baseline-config-empty.json +) diff --git a/go/codeql-tools/configure-baseline.sh b/go/codeql-tools/configure-baseline.sh new file mode 100755 index 00000000000..4883045f342 --- /dev/null +++ b/go/codeql-tools/configure-baseline.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ -f vendor/modules.txt ]; then + cat $CODEQL_EXTRACTOR_GO_ROOT/tools/baseline-config-vendor.json +else + cat $CODEQL_EXTRACTOR_GO_ROOT/tools/baseline-config-empty.json +fi diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 92bc9a062ad..7477a48f7b2 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.2 + +### Minor Analysis Improvements + +* Logrus' `WithContext` methods are no longer treated as if they output the values stored in that context to a log message. + ## 0.6.1 ### New Features diff --git a/go/ql/lib/change-notes/released/0.6.2.md b/go/ql/lib/change-notes/released/0.6.2.md new file mode 100644 index 00000000000..89ba2910d9d --- /dev/null +++ b/go/ql/lib/change-notes/released/0.6.2.md @@ -0,0 +1,5 @@ +## 0.6.2 + +### Minor Analysis Improvements + +* Logrus' `WithContext` methods are no longer treated as if they output the values stored in that context to a log message. diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 80fb0899f64..5501a2a1cc5 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.1 +lastReleaseVersion: 0.6.2 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index c67e35c49cf..a39d0b28edd 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/go-all -version: 0.6.1 +version: 0.6.2 groups: go dbscheme: go.dbscheme extractor: go library: true upgrades: upgrades dependencies: + codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/go/ql/lib/semmle/go/dataflow/DataFlow.qll b/go/ql/lib/semmle/go/dataflow/DataFlow.qll index fde408a4450..4a5290255a4 100644 --- a/go/ql/lib/semmle/go/dataflow/DataFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/DataFlow.qll @@ -22,7 +22,9 @@ import go * data flow analysis. */ module DataFlow { - import semmle.go.dataflow.internal.DataFlow + private import semmle.go.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.go.dataflow.internal.DataFlowImpl1 import Properties } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll index ea768f54715..83a6ca6e5ee 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll @@ -92,7 +92,7 @@ private DataFlowCallable getRestrictedInterfaceTarget(DataFlow::CallNode call) { /** * Gets a function that might be called by `call`. */ -DataFlowCallable viableCallable(CallExpr ma) { +DataFlowCallable viableCallable(DataFlowCall ma) { exists(DataFlow::CallNode call | call.asExpr() = ma | if isConcreteInterfaceCall(call, _, _) then result = getConcreteTarget(call) @@ -149,9 +149,10 @@ predicate golangSpecificParamArgFilter( // Interface methods calls may be passed strictly to that exact method's model receiver: arg.getPosition() != -1 or - exists(Function callTarget | callTarget = call.getNode().(DataFlow::CallNode).getTarget() | - not isInterfaceMethod(callTarget) - or - callTarget = p.getCallable().asSummarizedCallable().asFunction() - ) + p instanceof DataFlow::SummarizedParameterNode + or + not isInterfaceMethod(call.getNode() + .(DataFlow::CallNode) + .getACalleeWithoutVirtualDispatch() + .asFunction()) } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..b95eab3eb01 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..8f8f7b0a36c 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll index b0de9745816..1975ac9781f 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll index ee044c5e426..bc24ff09bfd 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides Go-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,12 @@ module Private { module Public { import DataFlowUtil } + +module GoDataFlow implements InputSig { + import Private + import Public + + predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1; + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 59224024ec3..5a51f16b83a 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -489,13 +489,9 @@ module Public { * interface type. */ Callable getACalleeIncludingExternals() { - result.asFunction() = this.getTarget() + result = this.getACalleeWithoutVirtualDispatch() or exists(DataFlow::Node calleeSource | calleeSource = this.getACalleeSource() | - result.asFuncLit() = calleeSource.asExpr() - or - calleeSource = result.asFunction().getARead() - or exists(Method declared, Method actual | calleeSource = declared.getARead() and actual.implements(declared) and @@ -510,6 +506,19 @@ module Public { */ FuncDef getACallee() { result = this.getACalleeIncludingExternals().getFuncDef() } + /** + * Gets the definition of a possible target of this call, excluding targets reachable via virtual dispatch. + */ + Callable getACalleeWithoutVirtualDispatch() { + result.asFunction() = this.getTarget() + or + exists(DataFlow::Node calleeSource | calleeSource = this.getACalleeSource() | + result.asFuncLit() = calleeSource.asExpr() + or + calleeSource = result.asFunction().getARead() + ) + } + /** * Gets the name of the function, method or variable that is being called. * diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll index 99d22d5c4e8..4c718bbe279 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -138,7 +138,7 @@ predicate jumpStep(Node n1, Node n2) { * Thus, `node2` references an object with a content `x` that contains the * value of `node1`. */ -predicate storeStep(Node node1, Content c, Node node2) { +predicate storeStep(Node node1, ContentSet c, Node node2) { // a write `(*p).f = rhs` is modeled as two store steps: `rhs` is flows into field `f` of `(*p)`, // which in turn flows into the pointer content of `p` exists(Write w, Field f, DataFlow::Node base, DataFlow::Node rhs | w.writesField(base, f, rhs) | @@ -165,7 +165,7 @@ predicate storeStep(Node node1, Content c, Node node2) { * Thus, `node1` references an object with a content `c` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content c, Node node2) { +predicate readStep(Node node1, ContentSet c, Node node2) { node1 = node2.(PointerDereferenceNode).getOperand() and c = any(DataFlow::PointerContent pc | pc.getPointerType() = node1.getType()) or @@ -184,7 +184,7 @@ predicate readStep(Node node1, Content c, Node node2) { /** * Holds if values stored inside content `c` are cleared at node `n`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { // Because our post-update nodes are shared between multiple pre-update // nodes, attempting to clear content causes summary stores into arg in // particular to malfunction. @@ -371,8 +371,6 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { getAFalsifiedGuard(call).dominates(n.getBasicBlock()) } -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll b/go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/go/ql/lib/semmle/go/frameworks/Logrus.qll b/go/ql/lib/semmle/go/frameworks/Logrus.qll index 9b93049acfb..30913423487 100644 --- a/go/ql/lib/semmle/go/frameworks/Logrus.qll +++ b/go/ql/lib/semmle/go/frameworks/Logrus.qll @@ -15,9 +15,7 @@ module Logrus { } bindingset[result] - private string getAnEntryUpdatingMethodName() { - result.regexpMatch("With(Context|Error|Fields?|Time)") - } + private string getAnEntryUpdatingMethodName() { result.regexpMatch("With(Error|Fields?|Time)") } private class LogFunction extends Function { LogFunction() { diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index aa3b9019d46..4fd67d3e427 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.2 + +No user-facing changes. + ## 0.6.1 No user-facing changes. diff --git a/go/ql/src/change-notes/released/0.6.2.md b/go/ql/src/change-notes/released/0.6.2.md new file mode 100644 index 00000000000..43f80640fc5 --- /dev/null +++ b/go/ql/src/change-notes/released/0.6.2.md @@ -0,0 +1,3 @@ +## 0.6.2 + +No user-facing changes. diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 80fb0899f64..5501a2a1cc5 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.1 +lastReleaseVersion: 0.6.2 diff --git a/go/ql/src/experimental/CWE-203/Timing.ql b/go/ql/src/experimental/CWE-203/Timing.ql index a22fd8727cd..b9cfa0f1a6d 100644 --- a/go/ql/src/experimental/CWE-203/Timing.ql +++ b/go/ql/src/experimental/CWE-203/Timing.ql @@ -24,16 +24,38 @@ private predicate isBadResult(DataFlow::Node e) { */ abstract class Sink extends DataFlow::Node { } -/** A taint-tracking sink which models comparisons of sensitive variables. */ -private class SensitiveCompareSink extends Sink { - ComparisonExpr c; +/** A taint-tracking sink which models comparisons of sensitive expressions using `strings.Compare` function. */ +private class SensitiveStringCompareSink extends Sink { + SensitiveStringCompareSink() { + // We select a comparison where a secret or password is tested. + exists(DataFlow::CallNode c, Expr op1, Expr nonSensitiveOperand | + c.getTarget().hasQualifiedName("strings", "Compare") and + c.getArgument(_).asExpr() = op1 and + op1.(SensitiveVariableAccess).getClassification() = + [SensitiveExpr::secret(), SensitiveExpr::password()] and + c.getArgument(_).asExpr() = nonSensitiveOperand and + not op1 = nonSensitiveOperand and + not ( + // Comparisons with `nil` should be excluded. + nonSensitiveOperand = Builtin::nil().getAReference() + or + // Comparisons with empty string should also be excluded. + nonSensitiveOperand.getStringValue().length() = 0 + ) + | + // It is important to note that the name of both the operands need not be + // `sensitive`. Even if one of the operands appears to be sensitive, we consider it a potential sink. + nonSensitiveOperand = this.asExpr() + ) + } +} +/** A taint-tracking sink which models comparisons of sensitive expressions. */ +private class SensitiveCompareSink extends Sink { SensitiveCompareSink() { // We select a comparison where a secret or password is tested. - exists(SensitiveVariableAccess op1, Expr op2 | + exists(SensitiveExpr op1, Expr op2, EqualityTestExpr c | op1.getClassification() = [SensitiveExpr::secret(), SensitiveExpr::password()] and - // exclude grant to avoid FP from OAuth - not op1.getClassification().matches("%grant%") and op1 = c.getAnOperand() and op2 = c.getAnOperand() and not op1 = op2 and @@ -45,13 +67,34 @@ private class SensitiveCompareSink extends Sink { op2.getStringValue().length() = 0 ) | - // It is important to note that the name of both the operands need not be - // `sensitive`. Even if one of the operands appears to be sensitive, we consider it a potential sink. - c.getAnOperand() = this.asExpr() + op2 = this.asExpr() ) } +} - DataFlow::Node getOtherOperand() { result.asExpr() = c.getAnOperand() and not result = this } +/** A taint-tracking sink which models comparisons of sensitive strings. */ +private class SensitiveStringSink extends Sink { + SensitiveStringSink() { + // We select a comparison where a secret or password is tested. + exists(StringLit op1, Expr op2, EqualityTestExpr c | + op1.getStringValue() + .regexpMatch(HeuristicNames::maybeSensitive([ + SensitiveExpr::secret(), SensitiveExpr::password() + ])) and + op1 = c.getAnOperand() and + op2 = c.getAnOperand() and + not op1 = op2 and + not ( + // Comparisons with `nil` should be excluded. + op2 = Builtin::nil().getAReference() + or + // Comparisons with empty string should also be excluded. + op2.getStringValue().length() = 0 + ) + | + op2 = this.asExpr() + ) + } } class SecretTracking extends TaintTracking::Configuration { @@ -65,8 +108,6 @@ class SecretTracking extends TaintTracking::Configuration { } from SecretTracking cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where - cfg.hasFlowPath(source, sink) and - not cfg.hasFlowTo(sink.getNode().(SensitiveCompareSink).getOtherOperand()) +where cfg.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ may be vulnerable to timing attacks.", source.getNode(), "Hardcoded String" diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 0da34268dc8..8ac7f37e30c 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.6.1 +version: 0.6.2 groups: - go - queries diff --git a/go/ql/test/experimental/CWE-203/Timing.expected b/go/ql/test/experimental/CWE-203/Timing.expected index a94866cda5a..05e19774dbc 100644 --- a/go/ql/test/experimental/CWE-203/Timing.expected +++ b/go/ql/test/experimental/CWE-203/Timing.expected @@ -1,10 +1,22 @@ edges -| timing.go:14:18:14:27 | selection of Header | timing.go:14:18:14:45 | call to Get | -| timing.go:14:18:14:45 | call to Get | timing.go:16:25:16:36 | headerSecret | +| timing.go:15:18:15:27 | selection of Header | timing.go:15:18:15:45 | call to Get | +| timing.go:15:18:15:45 | call to Get | timing.go:17:31:17:42 | headerSecret | +| timing.go:28:18:28:27 | selection of Header | timing.go:28:18:28:45 | call to Get | +| timing.go:28:18:28:45 | call to Get | timing.go:30:47:30:58 | headerSecret | +| timing.go:41:18:41:27 | selection of Header | timing.go:41:18:41:45 | call to Get | +| timing.go:41:18:41:45 | call to Get | timing.go:42:25:42:36 | headerSecret | nodes -| timing.go:14:18:14:27 | selection of Header | semmle.label | selection of Header | -| timing.go:14:18:14:45 | call to Get | semmle.label | call to Get | -| timing.go:16:25:16:36 | headerSecret | semmle.label | headerSecret | +| timing.go:15:18:15:27 | selection of Header | semmle.label | selection of Header | +| timing.go:15:18:15:45 | call to Get | semmle.label | call to Get | +| timing.go:17:31:17:42 | headerSecret | semmle.label | headerSecret | +| timing.go:28:18:28:27 | selection of Header | semmle.label | selection of Header | +| timing.go:28:18:28:45 | call to Get | semmle.label | call to Get | +| timing.go:30:47:30:58 | headerSecret | semmle.label | headerSecret | +| timing.go:41:18:41:27 | selection of Header | semmle.label | selection of Header | +| timing.go:41:18:41:45 | call to Get | semmle.label | call to Get | +| timing.go:42:25:42:36 | headerSecret | semmle.label | headerSecret | subpaths #select -| timing.go:16:25:16:36 | headerSecret | timing.go:14:18:14:27 | selection of Header | timing.go:16:25:16:36 | headerSecret | $@ may be vulnerable to timing attacks. | timing.go:14:18:14:27 | selection of Header | Hardcoded String | +| timing.go:17:31:17:42 | headerSecret | timing.go:15:18:15:27 | selection of Header | timing.go:17:31:17:42 | headerSecret | $@ may be vulnerable to timing attacks. | timing.go:15:18:15:27 | selection of Header | Hardcoded String | +| timing.go:30:47:30:58 | headerSecret | timing.go:28:18:28:27 | selection of Header | timing.go:30:47:30:58 | headerSecret | $@ may be vulnerable to timing attacks. | timing.go:28:18:28:27 | selection of Header | Hardcoded String | +| timing.go:42:25:42:36 | headerSecret | timing.go:41:18:41:27 | selection of Header | timing.go:42:25:42:36 | headerSecret | $@ may be vulnerable to timing attacks. | timing.go:41:18:41:27 | selection of Header | Hardcoded String | diff --git a/go/ql/test/experimental/CWE-203/timing.go b/go/ql/test/experimental/CWE-203/timing.go index 627d1a59a36..43401bd4111 100644 --- a/go/ql/test/experimental/CWE-203/timing.go +++ b/go/ql/test/experimental/CWE-203/timing.go @@ -4,6 +4,7 @@ import ( "crypto/subtle" "fmt" "net/http" + "strings" ) func bad(w http.ResponseWriter, req *http.Request) (interface{}, error) { @@ -13,7 +14,32 @@ func bad(w http.ResponseWriter, req *http.Request) (interface{}, error) { headerSecret := req.Header.Get(secretHeader) secretStr := string(secret) - if len(secret) != 0 && headerSecret != secretStr { + if len(headerSecret) != 0 && headerSecret != secretStr { + return nil, fmt.Errorf("header %s=%s did not match expected secret", secretHeader, headerSecret) + } + return nil, nil +} + +func bad2(w http.ResponseWriter, req *http.Request) (interface{}, error) { + + secret := "MySuperSecretPasscode" + secretHeader := "X-Secret" + + headerSecret := req.Header.Get(secretHeader) + secretStr := string(secret) + if len(headerSecret) != 0 && strings.Compare(headerSecret, secretStr) != 0 { + return nil, fmt.Errorf("header %s=%s did not match expected secret", secretHeader, headerSecret) + } + return nil, nil +} + +func bad4(w http.ResponseWriter, req *http.Request) (interface{}, error) { + + secret := "MySuperSecretPasscode" + secretHeader := "X-Secret" + + headerSecret := req.Header.Get(secretHeader) + if len(secret) != 0 && headerSecret != "SecretStringLiteral" { return nil, fmt.Errorf("header %s=%s did not match expected secret", secretHeader, headerSecret) } return nil, nil @@ -34,4 +60,6 @@ func good(w http.ResponseWriter, req *http.Request) (interface{}, error) { func main() { bad(nil, nil) good(nil, nil) + bad2(nil, nil) + bad4(nil, nil) } diff --git a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go index 9f85565ad42..ce2d3ba4e25 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go +++ b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go @@ -18,7 +18,7 @@ func logrusCalls() { var fields logrus.Fields = nil var fn logrus.LogFunction = nil var ctx context.Context - tmp := logrus.WithContext(ctx) // $ logger=ctx + tmp := logrus.WithContext(ctx) // ctx isn't output, so no match here tmp.Debugf(fmt, text) // $ logger=fmt logger=text tmp = logrus.WithError(err) // $ logger=err tmp.Warn(text) // $ logger=text diff --git a/go/ql/test/query-tests/Diagnostics/CONSISTENCY/UnexpectedFrontendErrors.expected b/go/ql/test/query-tests/Diagnostics/CONSISTENCY/UnexpectedFrontendErrors.expected index a608f356a30..909c0a71ede 100644 --- a/go/ql/test/query-tests/Diagnostics/CONSISTENCY/UnexpectedFrontendErrors.expected +++ b/go/ql/test/query-tests/Diagnostics/CONSISTENCY/UnexpectedFrontendErrors.expected @@ -5,5 +5,4 @@ | bad.go:3:5:3:5 | expected 'IDENT', found newline | | bad.go:5:1:5:1 | expected ';', found wnvwun | | badimport.go:6:2:6:2 | invalid import path (invalid character U+007B '{') | -| badimport.go:6:2:6:2 | malformed import path "github.com/pkg{}": invalid char '{' | | type.go:11:9:11:9 | cannot use v (variable of type V) as T value in argument to takesT | diff --git a/go/ql/test/query-tests/Diagnostics/ExtractionErrors.expected b/go/ql/test/query-tests/Diagnostics/ExtractionErrors.expected index 52fc92b4973..c7695f79dca 100644 --- a/go/ql/test/query-tests/Diagnostics/ExtractionErrors.expected +++ b/go/ql/test/query-tests/Diagnostics/ExtractionErrors.expected @@ -1,5 +1,4 @@ | Extraction failed in query-tests/Diagnostics/badimport.go with error invalid import path (invalid character U+007B '{') | 2 | -| Extraction failed in query-tests/Diagnostics/badimport.go with error malformed import path "github.com/pkg{}": invalid char '{' | 2 | | Extraction failed in query-tests/Diagnostics/type.go with error cannot use v (variable of type V) as T value in argument to takesT | 2 | | Extraction failed with error expected ';', found wnvwun | 2 | | Extraction failed with error expected 'IDENT', found newline | 2 | diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 1681b511d92..272a1b85896 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -1,4 +1,5 @@ package,sink,source,summary,sink:bean-validation,sink:command-injection,sink:file-content-store,sink:fragment-injection,sink:groovy-injection,sink:hostname-verification,sink:html-injection,sink:information-leak,sink:intent-redirection,sink:jexl-injection,sink:jndi-injection,sink:js-injection,sink:ldap-injection,sink:log-injection,sink:mvel-injection,sink:ognl-injection,sink:path-injection,sink:pending-intents,sink:regex-use,sink:regex-use[-1],sink:regex-use[0],sink:regex-use[],sink:regex-use[f-1],sink:regex-use[f1],sink:regex-use[f],sink:request-forgery,sink:response-splitting,sink:sql-injection,sink:template-injection,sink:url-redirection,sink:xpath-injection,sink:xslt-injection,source:android-external-storage-dir,source:contentprovider,source:remote,summary:taint,summary:value +actions.osgi,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, android.app,35,,103,,,,11,,,,,7,,,,,,,,,17,,,,,,,,,,,,,,,,,,18,85 android.content,24,31,154,,,,,,,,,16,,,,,,,,,,,,,,,,,,,8,,,,,4,27,,63,91 android.database,59,,41,,,,,,,,,,,,,,,,,,,,,,,,,,,,59,,,,,,,,41, @@ -27,7 +28,7 @@ com.google.gson,,,44,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30,14 com.hubspot.jinjava,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,, com.jcraft.jsch,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1, com.mitchellbosecke.pebble,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,, -com.opensymphony.xwork2.ognl,3,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,, +com.opensymphony.xwork2,67,,961,,,,,,,,,,,,,,,,67,,,,,,,,,,,,,,,,,,,,867,94 com.rabbitmq.client,,21,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,7, com.thoughtworks.xstream,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,, com.unboundid.ldap.sdk,17,,,,,,,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,,, @@ -55,7 +56,7 @@ jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,94,55 java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3 java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -java.io,50,,45,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,43,2 +java.io,50,,46,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,44,2 java.lang,31,,93,,13,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,57,36 java.net,13,3,23,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,23, java.nio,53,,36,,,5,,,,,,,,,,,,,,47,,,,,,,,,1,,,,,,,,,,36, @@ -121,6 +122,8 @@ org.apache.log4j,11,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,, org.apache.logging.log4j,359,,8,,,,,,,,,,,,,,359,,,,,,,,,,,,,,,,,,,,,,4,4 org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, org.apache.shiro.jndi,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.struts.beanvalidation.validation.interceptor,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, +org.apache.struts2,11,,3873,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,3839,34 org.apache.tools.ant,11,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,, org.apache.tools.zip,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, org.apache.velocity.app,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index d07f04c3897..99efe927a47 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -18,10 +18,10 @@ Java framework & library support `Google Guava `_,``com.google.common.*``,,730,41,7,,,,, JBoss Logging,``org.jboss.logging``,,,324,,,,,, `JSON-java `_,``org.json``,,236,,,,,,, - Java Standard Library,``java.*``,3,688,205,80,,9,,,18 + Java Standard Library,``java.*``,3,689,205,80,,9,,,18 Java extensions,"``javax.*``, ``jakarta.*``",63,672,34,2,4,,1,1,2 Kotlin Standard Library,``kotlin*``,,1849,16,14,,,,,2 `Spring `_,``org.springframework.*``,29,483,115,4,,28,14,,35 - Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,5237,577,89,6,18,18,,200 - Totals,,283,13606,2067,290,16,122,33,1,391 + Others,"``actions.osgi``, ``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.struts.beanvalidation.validation.interceptor``, ``org.apache.struts2``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,10081,652,89,6,18,18,,200 + Totals,,283,18451,2142,290,16,122,33,1,391 diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index aa0a137173b..06a5b4bb48c 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -552,7 +552,7 @@ open class KotlinFileExtractor( logger.warnElement("Expected annotation property to define a getter", prop) } else { val getterId = useFunction(getter) - val exprId = extractAnnotationValueExpression(v, id, i, "{${getterId}}", getter.returnType, extractEnumTypeAccesses) + val exprId = extractAnnotationValueExpression(v, id, i, "{$getterId}", getter.returnType, extractEnumTypeAccesses) if (exprId != null) { tw.writeAnnotValue(id, getterId, exprId) } @@ -587,7 +587,7 @@ open class KotlinFileExtractor( extractAnnotation(v, parent, idx, extractEnumTypeAccesses, contextLabel) } is IrVararg -> { - tw.getLabelFor("@\"annotationarray;{${parent}};$contextLabel\"").also { arrayId -> + tw.getLabelFor("@\"annotationarray;{$parent};$contextLabel\"").also { arrayId -> // Use the context type (i.e., the type the annotation expects, not the actual type of the array) // because the Java extractor fills in array types using the same technique. These should only // differ for generic annotations. @@ -1193,7 +1193,7 @@ open class KotlinFileExtractor( // n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list. // Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver // will be prepended if one exists. - val realFunctionId = useFunction(f) + val realFunctionId = useFunction(f, parentId, null) DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf(), isStatic = true)).use { val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2) val intType = pluginContext.irBuiltIns.intType diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index fdaebe5f1c8..95b7dd6d28d 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -612,7 +612,7 @@ open class KotlinUsesExtractor( val componentTypeLabel = recInfo.componentTypeResults.javaResult.id val dimensions = recInfo.dimensions + 1 - val id = tw.getLabelFor("@\"array;$dimensions;{${elementTypeLabel}}\"") { + val id = tw.getLabelFor("@\"array;$dimensions;{$elementTypeLabel}\"") { tw.writeArrays( it, javaShortName, @@ -1141,7 +1141,7 @@ open class KotlinUsesExtractor( // method (and presumably that disambiguation is never needed when the method belongs to a parameterized // instance of a generic class), but as of now I don't know when the raw method would be referred to. val typeArgSuffix = if (functionTypeParameters.isNotEmpty() && classTypeArgsIncludingOuterClasses.isNullOrEmpty()) "<${functionTypeParameters.size}>" else ""; - return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}${typeArgSuffix}\"" + return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}$typeArgSuffix\"" } val javaLangClass by lazy { referenceExternalClass("java.lang.Class") } @@ -1672,7 +1672,7 @@ open class KotlinUsesExtractor( // clashing trap labels. These are always private, so we can just make up a label without // worrying about their names as seen from Java. val extensionPropertyDiscriminator = getExtensionReceiverType(f)?.let { "extension;${useType(it).javaResult.id}" } ?: "" - return "@\"field;{$parentId};${extensionPropertyDiscriminator}${f.name.asString()}\"" + return "@\"field;{$parentId};$extensionPropertyDiscriminator${f.name.asString()}\"" } fun useField(f: IrField): Label = diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 2dee392247e..4e4387964e6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -46,12 +46,22 @@ class LogMessage(private val kind: String, private val message: String) { private fun escape(str: String): String { return str.replace("\\", "\\\\") .replace("\"", "\\\"") - .replace("/", "\\/") - .replace("\b", "\\b") + .replace("\u0000", "\\u0000") + .replace("\u0001", "\\u0001") + .replace("\u0002", "\\u0002") + .replace("\u0003", "\\u0003") + .replace("\u0004", "\\u0004") + .replace("\u0005", "\\u0005") + .replace("\u0006", "\\u0006") + .replace("\u0007", "\\u0007") + .replace("\u0008", "\\b") + .replace("\u0009", "\\t") + .replace("\u000A", "\\n") + .replace("\u000B", "\\u000B") .replace("\u000C", "\\f") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("\t", "\\t") + .replace("\u000D", "\\r") + .replace("\u000E", "\\u000E") + .replace("\u000F", "\\u000F") } fun toJsonLine(): String { diff --git a/java/ql/consistency-queries/diags.ql b/java/ql/consistency-queries/diags.ql index 799d089e471..6d96dd2eb96 100644 --- a/java/ql/consistency-queries/diags.ql +++ b/java/ql/consistency-queries/diags.ql @@ -28,7 +28,8 @@ string diagnosticMessage(Diagnostic d) { // something is fixed. query predicate unusedDiagnosticException(DiagnosticException de) { not exists(de.getException()) } -query predicate unexpectedDiagnostic(Diagnostic d, string s) { +query predicate unexpectedDiagnostic(Compilation c, int f, int i, Diagnostic d, string s) { + d.getCompilationInfo(c, f, i) and s = diagnosticMessage(d) and not d = any(DiagnosticException de).getException() } diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index b6315cf80e4..d2f6119868f 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.7.2 + +### New Features + +* A `Diagnostic.getCompilationInfo()` predicate has been added. + +### Minor Analysis Improvements + +* Fixed a typo in the `StdlibRandomSource` class in `RandomDataSource.qll`, which caused the class to improperly model calls to the `nextBytes` method. Queries relying on `StdlibRandomSource` may see an increase in results. +* Improved the precision of virtual dispatch of `java.io.InputStream` methods. Now, calls to these methods will not dispatch to arbitrary implementations of `InputStream` if there is a high-confidence alternative (like a models-as-data summary). +* Added more dataflow steps for `java.io.InputStream`s that wrap other `java.io.InputStream`s. +* Added models for the Struts 2 framework. +* Improved the modeling of Struts 2 sources of untrusted data by tainting the whole object graph of the objects unmarshaled from an HTTP request. + ## 0.7.1 ### New Features diff --git a/java/ql/lib/change-notes/released/0.7.2.md b/java/ql/lib/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..1a19854d503 --- /dev/null +++ b/java/ql/lib/change-notes/released/0.7.2.md @@ -0,0 +1,13 @@ +## 0.7.2 + +### New Features + +* A `Diagnostic.getCompilationInfo()` predicate has been added. + +### Minor Analysis Improvements + +* Fixed a typo in the `StdlibRandomSource` class in `RandomDataSource.qll`, which caused the class to improperly model calls to the `nextBytes` method. Queries relying on `StdlibRandomSource` may see an increase in results. +* Improved the precision of virtual dispatch of `java.io.InputStream` methods. Now, calls to these methods will not dispatch to arbitrary implementations of `InputStream` if there is a high-confidence alternative (like a models-as-data summary). +* Added more dataflow steps for `java.io.InputStream`s that wrap other `java.io.InputStream`s. +* Added models for the Struts 2 framework. +* Improved the modeling of Struts 2 sources of untrusted data by tainting the whole object graph of the objects unmarshaled from an HTTP request. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/java/ql/lib/ext/generated/struts2.model.yml b/java/ql/lib/ext/generated/struts2.model.yml new file mode 100644 index 00000000000..74e1101a960 --- /dev/null +++ b/java/ql/lib/ext/generated/struts2.model.yml @@ -0,0 +1,6881 @@ +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of models for the Struts Models framework. +extensions: + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["actions.osgi", "HelloWorldAction", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["actions.osgi", "HelloWorldAction", true, "setMessage", "(Message)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["actions.osgi", "HelloWorldAction", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["actions.osgi", "Message", true, "Message", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["actions.osgi", "Message", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["actions.osgi", "Message", true, "setText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "Builder", "(ActionConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addAllowedMethod", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addAllowedMethod", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addAllowedMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addAllowedMethod", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addExceptionMapping", "(ExceptionMappingConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addExceptionMappings", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addResultConfig", "(ResultConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addResultConfigs", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "addResultConfigs", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "className", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "className", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "defaultClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "defaultClassName", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "exceptionMappings", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "interceptors", "(List)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "methodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "methodName", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "packageName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "packageName", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "setDefaultMethodRegex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "setDefaultMethodRegex", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig$Builder", true, "setStrictMethodInvocation", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getExceptionMappings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getInterceptors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getPackageName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "getResults", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", true, "build", "(boolean,Set,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", true, "getDefaultRegex", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "Builder", "(ExceptionMappingConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "Builder", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "exceptionClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "exceptionClassName", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "result", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig$Builder", true, "result", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig", true, "getExceptionClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig", true, "getResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ExceptionMappingConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "Builder", "(InterceptorConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "Builder", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "Builder", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "className", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "className", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig$Builder", false, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig", true, "getClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorListHolder", true, "addInterceptor", "(InterceptorMapping)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorListHolder", true, "addInterceptors", "(List)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorLocator", true, "getInterceptorConfig", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "InterceptorMapping", "(String,Interceptor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "InterceptorMapping", "(String,Interceptor)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "InterceptorMapping", "(String,Interceptor,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "InterceptorMapping", "(String,Interceptor,Map)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "InterceptorMapping", "(String,Interceptor,Map)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "getInterceptor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorMapping", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "Builder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig$Builder", true, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig", true, "getInterceptors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "InterceptorStackConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "Builder", "(PackageConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "Builder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addActionConfig", "(String,ActionConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addExceptionMappingConfig", "(ExceptionMappingConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addGlobalAllowedMethods", "(Set)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addGlobalExceptionMappingConfigs", "(List)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addGlobalResultConfig", "(ResultConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addGlobalResultConfigs", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addInterceptorConfig", "(InterceptorConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addInterceptorStackConfig", "(InterceptorStackConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addParent", "(PackageConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addParents", "(List)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "addResultTypeConfig", "(ResultTypeConfig)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultActionRef", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultActionRef", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultClassRef", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultClassRef", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultInterceptorRef", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultInterceptorRef", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultResultType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "defaultResultType", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getDefaultClassRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getFullDefaultResultType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getGlobalAllowedMethods", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getParentsAllowedMethods", "(List)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "getResultType", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "isAbstract", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "namespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "namespace", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "needsRefresh", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "strictMethodInvocation", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getActionConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getAllActionConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getAllExceptionMappingConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getAllGlobalResults", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getAllInterceptorConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getAllResultTypeConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getDefaultActionRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getDefaultClassRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getDefaultInterceptorRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getDefaultResultType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getFullDefaultActionRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getFullDefaultInterceptorRef", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getFullDefaultResultType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getGlobalAllowedMethods", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getGlobalExceptionMappingConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getGlobalResultConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getInterceptorConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getParents", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "getResultTypeConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "Builder", "(ResultConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "Builder", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "Builder", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "className", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "className", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig$Builder", false, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig", true, "getClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "Builder", "(ResultTypeConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "Builder", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "Builder", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "className", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "className", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "defaultResultParam", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "defaultResultParam", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "name", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig$Builder", false, "name", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig", true, "getClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig", true, "getDefaultResultParam", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ResultTypeConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "UnknownHandlerConfig", true, "UnknownHandlerConfig", "(String,Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "UnknownHandlerConfig", true, "UnknownHandlerConfig", "(String,Location)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "UnknownHandlerConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "UnknownHandlerConfig", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "UnknownHandlerConfig", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "AbstractMatcher", true, "AbstractMatcher", "(PatternMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "AbstractMatcher", true, "AbstractMatcher", "(PatternMatcher,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "ActionConfigMatcher", true, "ActionConfigMatcher", "(PatternMatcher,Map,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "ActionConfigMatcher", true, "ActionConfigMatcher", "(PatternMatcher,Map,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "DefaultConfiguration", true, "DefaultConfiguration", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableConstantFactory", true, "LocatableConstantFactory", "(Object,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableConstantFactory", true, "LocatableConstantFactory", "(Object,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableConstantFactory", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableFactory", true, "LocatableFactory", "(String,Class,Class,Scope,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableFactory", true, "LocatableFactory", "(String,Class,Class,Scope,Object)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "LocatableFactory", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatch", true, "NamespaceMatch", "(String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatch", true, "NamespaceMatch", "(String,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatch", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatch", true, "getVariables", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatcher", true, "NamespaceMatcher", "(PatternMatcher,Set)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "NamespaceMatcher", true, "NamespaceMatcher", "(PatternMatcher,Set,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "CycleDetector", true, "CycleDetector", "(DirectedGraph)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "CycleDetector", true, "getVerticesInCycles", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", false, "addNode", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", false, "edgesFrom", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "InterceptorBuilder", true, "constructInterceptorReference", "(InterceptorLocator,String,Map,Location,ObjectFactory)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "InterceptorBuilder", true, "constructInterceptorReference", "(InterceptorLocator,String,Map,Location,ObjectFactory)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "InterceptorBuilder", true, "constructInterceptorReference", "(InterceptorLocator,String,Map,Location,ObjectFactory)", "", "Argument[2].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "ValueSubstitutor", true, "substitute", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "ValueSubstitutor", true, "substitute", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlConfigurationProvider", true, "XmlConfigurationProvider", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlConfigurationProvider", true, "XmlConfigurationProvider", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlConfigurationProvider", true, "setFileManagerFactory", "(FileManagerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlConfigurationProvider", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", true, "getDtdMappings", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", true, "setDtdMappings", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", true, "setValueSubstitutor", "(ValueSubstitutor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "addPackageConfig", "(String,PackageConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "addPackageConfig", "(String,PackageConfig)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getContainer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getLoadedFileNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getPackageConfig", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getPackageConfigNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getPackageConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getRuntimeConfiguration", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "getUnknownHandlerStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "reloadContainer", "(List)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "reloadContainer", "(List)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "removePackageConfig", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", true, "setUnknownHandlerStack", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(String,Throwable,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationException", true, "ConfigurationException", "(Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "ConfigurationManager", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "addContainerProvider", "(ContainerProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "getConfiguration", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "getContainerProviders", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", true, "setContainerProviders", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationUtil", true, "buildParentListFromString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationUtil", true, "buildParentsFromString", "(Configuration,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", true, "init", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", true, "register", "(ContainerBuilder,LocatableProperties)", "", "Argument[this]", "Argument[1].Element", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "FileManagerProvider", true, "FileManagerProvider", "(Class,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "PackageProvider", true, "init", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "RuntimeConfiguration", true, "getActionConfig", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.config", "RuntimeConfiguration", true, "getActionConfigs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "CollectionConverter", true, "setObjectTypeDeterminer", "(ObjectTypeDeterminer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "ConversionData", true, "ConversionData", "(Object,Class)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "ConversionData", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "ConversionData", true, "setValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultConversionAnnotationProcessor", true, "setTypeConverterCreator", "(TypeConverterCreator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultConversionAnnotationProcessor", true, "setTypeConverterHolder", "(TypeConverterHolder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultConversionFileProcessor", true, "setFileManagerFactory", "(FileManagerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultConversionFileProcessor", true, "setTypeConverterCreator", "(TypeConverterCreator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultObjectTypeDeterminer", true, "DefaultObjectTypeDeterminer", "(XWorkConverter,ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultObjectTypeDeterminer", true, "DefaultObjectTypeDeterminer", "(XWorkConverter,ReflectionProvider)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", true, "convertValue", "(Map,Object,Class)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", true, "convertValue", "(Object,Class)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", true, "stringValue", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", true, "stringValue", "(Object,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "InstantiatingNullHandler", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "InstantiatingNullHandler", true, "setObjectTypeDeterminer", "(ObjectTypeDeterminer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "InstantiatingNullHandler", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "lookup", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "lookup", "(String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "setConversionAnnotationProcessor", "(ConversionAnnotationProcessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "setConversionFileProcessor", "(ConversionFileProcessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "setDefaultTypeConverter", "(XWorkBasicConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "setFileManagerFactory", "(FileManagerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", true, "setTypeConverterHolder", "(TypeConverterHolder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ConversionAnnotationProcessor", true, "process", "(Map,TypeConversion,String)", "", "Argument[2]", "Argument[0].Element", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "NullHandler", true, "nullPropertyValue", "(Map,Object,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ObjectTypeDeterminer", true, "getKeyProperty", "(Class,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverter", true, "convertValue", "(Map,Object,Member,String,Object,Class)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "addDefaultMapping", "(String,TypeConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "addDefaultMapping", "(String,TypeConverter)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "addMapping", "(Class,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "addUnknownMapping", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "getDefaultMapping", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", true, "getMapping", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "ActionFactory", true, "buildAction", "(String,String,ActionConfig,Map)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultActionFactory", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultInterceptorFactory", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultInterceptorFactory", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultResultFactory", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultResultFactory", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultUnknownHandlerFactory", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultValidatorFactory", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "DefaultValidatorFactory", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "InterceptorFactory", true, "buildInterceptor", "(InterceptorConfig,Map)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.factory", "StrutsConverterFactory", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject.util", "Strings", true, "capitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", true, "getInstanceNames", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", true, "setScopeStrategy", "(Strategy)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "alias", "(Class,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "alias", "(Class,String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,Enum)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "constant", "(String,short)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "create", "(boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,Class,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,Factory)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,Factory,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String,Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String,Class,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String,Factory)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String,Factory,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "factory", "(Class,String,Scope)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "injectStatics", "(Class[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", false, "injectStatics", "(Class[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Context", true, "getContainer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Context", true, "getMember", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Context", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "DependencyException", true, "DependencyException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "DependencyException", true, "DependencyException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "DependencyException", true, "DependencyException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.inject", "DependencyException", true, "DependencyException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", true, "setAcceptedPatterns", "(AcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", true, "setAliasesKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", true, "setExcludedPatterns", "(ExcludedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "getExcludes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "getIncludes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "setExcludes", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "setExcludesCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "setIncludes", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "setIncludesCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "DefaultWorkflowInterceptor", true, "setInputResultName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionHolder", true, "ExceptionHolder", "(Exception)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionHolder", true, "getException", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", true, "getLogCategory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", true, "getLogLevel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", true, "setLogCategory", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", true, "setLogLevel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", true, "intercept", "(ActionInvocation)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", true, "intercept", "(ActionInvocation)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", true, "intercept", "(ActionInvocation)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptor", true, "doIntercept", "(ActionInvocation)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptor", true, "getExcludeMethodsSet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptor", true, "getIncludeMethodsSet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptor", true, "setExcludeMethods", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptor", true, "setIncludeMethods", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "getAllowedCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "getBlockedCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "setAllowed", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "setAllowedCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "setBlocked", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", true, "setBlockedCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterRemoverInterceptor", true, "setParamNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterRemoverInterceptor", true, "setParamValues", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", true, "setAcceptedPatterns", "(AcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", true, "setExcludedPatterns", "(ExcludedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PrefixMethodInvocationUtil", true, "capitalizeMethodName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ScopedModelDrivenInterceptor", true, "setClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ScopedModelDrivenInterceptor", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ScopedModelDrivenInterceptor", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ScopedModelDrivenInterceptor", true, "setScope", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "addActionError", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "addActionMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "getActionErrors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "getActionMessages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "getFieldErrors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "setActionErrors", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "setActionMessages", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", true, "setFieldErrors", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "WithLazyParams$LazyParamInjector", true, "injectParams", "(Interceptor,Map,ActionContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "WithLazyParams$LazyParamInjector", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "WithLazyParams$LazyParamInjector", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "WithLazyParams$LazyParamInjector", true, "setTextParser", "(TextParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "getActionEventListener", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "setAction", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "setInvocationContext", "(ActionContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "setProxy", "(ActionProxy)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "setResult", "(Result)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionInvocation", true, "setStack", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setAction", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setConfig", "(ActionConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setInvocation", "(ActionInvocation)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", true, "setReturnedResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockInterceptor", true, "getExpectedFoo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockInterceptor", true, "getFoo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockInterceptor", true, "setExpectedFoo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockInterceptor", true, "setFoo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", true, "MockObjectTypeDeterminer", "(Class,Class,String,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", true, "getKeyProperty", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", true, "setKeyProperty", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockResult", true, "getInvocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkCollectionPropertyAccessor", true, "getPropertyThroughIteration", "(Map,Collection,String,Object)", "", "Argument[1].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkCollectionPropertyAccessor", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkCollectionPropertyAccessor", true, "setObjectTypeDeterminer", "(ObjectTypeDeterminer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkCollectionPropertyAccessor", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkCollectionPropertyAccessor", true, "setXWorkConverter", "(XWorkConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", true, "setObjectTypeDeterminer", "(ObjectTypeDeterminer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", true, "setXWorkCollectionPropertyAccessor", "(PropertyAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", true, "setXWorkConverter", "(XWorkConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkMapPropertyAccessor", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkMapPropertyAccessor", true, "setObjectTypeDeterminer", "(ObjectTypeDeterminer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkMapPropertyAccessor", true, "setXWorkConverter", "(XWorkConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ErrorMessageBuilder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ErrorMessageBuilder", true, "errorSettingExpressionWithValue", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ErrorMessageBuilder", true, "errorSettingExpressionWithValue", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ErrorMessageBuilder", true, "errorSettingExpressionWithValue", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", true, "getLastPropertyAccessed", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", true, "setLastPropertyAccessed", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", true, "setValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlNullHandlerWrapper", true, "OgnlNullHandlerWrapper", "(NullHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlReflectionProvider", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlTypeConverterWrapper", true, "OgnlTypeConverterWrapper", "(TypeConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlTypeConverterWrapper", true, "getTarget", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "compile", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "compile", "(String,Map)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getBeanInfo", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getBeanInfo", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getExcludedClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getExcludedPackageExemptClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getExcludedPackageNamePatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getExcludedPackageNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "getRealTarget", "(String,Map,Object)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setAcceptProperties", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setExcludeProperties", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setExcludedClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setExcludedPackageExemptClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setExcludedPackageNamePatterns", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", true, "setExcludedPackageNames", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "XWorkTypeConverterWrapper", true, "XWorkTypeConverterWrapper", "(TypeConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker$IsAccepted", false, "getAcceptedPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker$IsAccepted", false, "no", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker$IsAccepted", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker$IsAccepted", false, "yes", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker", true, "getAcceptedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "DefaultNotExcludedAcceptedPatternsChecker", true, "setAcceptedPatterns", "(AcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "DefaultNotExcludedAcceptedPatternsChecker", true, "setExcludedPatterns", "(ExcludedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker$IsExcluded", false, "getExcludedPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker$IsExcluded", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker", true, "getExcludedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker$IsAllowed", false, "getAllowedPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker$IsAllowed", false, "no", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker$IsAllowed", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker$IsAllowed", false, "yes", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object,AutowireCapableBeanFactory)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object,AutowireCapableBeanFactory)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", true, "autoWireBean", "(Object,AutowireCapableBeanFactory)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "AbstractResourceStore", true, "AbstractResourceStore", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "AbstractResourceStore", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "FileResourceStore", false, "FileResourceStore", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "JarResourceStore", true, "JarResourceStore", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ReloadingClassLoader", true, "ReloadingClassLoader", "(ClassLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ReloadingClassLoader", true, "addResourceStore", "(ResourceStore)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ReloadingClassLoader", true, "setAccepClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ResourceStoreClassLoader", false, "ResourceStoreClassLoader", "(ClassLoader,ResourceStore[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$Annotatable", true, "getAnnotations", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$AnnotationInfo", true, "AnnotationInfo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$AnnotationInfo", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "ClassInfo", "(Class,ClassFinder)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "ClassInfo", "(String,String,ClassFinder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "ClassInfo", "(String,String,ClassFinder)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "ClassInfo", "(String,String,ClassFinder)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getConstructors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getFields", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getInterfaces", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getMethods", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getPackageName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getSuperInterfaces", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "getSuperType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "FieldInfo", "(ClassInfo,Field)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "FieldInfo", "(ClassInfo,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "FieldInfo", "(ClassInfo,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "FieldInfo", "(ClassInfo,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "getDeclaringClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$FieldInfo", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$Info", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "MethodInfo", "(ClassInfo,Constructor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "MethodInfo", "(ClassInfo,Method)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "MethodInfo", "(ClassInfo,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "MethodInfo", "(ClassInfo,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "MethodInfo", "(ClassInfo,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "getDeclaringClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "getParameterAnnotations", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "getParameterAnnotations", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "getReturnType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$MethodInfo", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$PackageInfo", true, "PackageInfo", "(Package)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$PackageInfo", true, "PackageInfo", "(String,ClassFinder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$PackageInfo", true, "PackageInfo", "(String,ClassFinder)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$PackageInfo", true, "get", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", true, "findAnnotatedPackages", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", true, "getClassLoaderInterface", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", true, "getClassesNotLoaded", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterfaceDelegate", true, "ClassLoaderInterfaceDelegate", "(ClassLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,ClassLoaderInterface)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,ClassLoaderInterface)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,ClassLoaderInterface,URL[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,ClassLoaderInterface,URL[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,ClassLoaderInterface,URL[])", "", "Argument[2].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,URL[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(String,URL[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "ResourceFinder", "(URL[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", true, "getResourcesNotLoaded", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet$FileProtocolNormalizer", true, "normalizeToFileProtocol", "(URL)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "UrlSet", "(ClassLoaderInterface,Set)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "UrlSet", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "UrlSet", "(URL[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "exclude", "(ClassLoaderInterface)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "exclude", "(File)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "exclude", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "exclude", "(UrlSet)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "excludeJavaEndorsedDirs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "excludeJavaExtDirs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "excludeJavaHome", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "excludePaths", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "excludeUserExtensionsDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "getUrls", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "include", "(UrlSet)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "include", "(UrlSet)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "includeClassesUrl", "(ClassLoaderInterface,FileProtocolNormalizer)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "matching", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", true, "relative", "(File)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "DefaultFileManagerFactory", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "DefaultFileManagerFactory", true, "setFileManager", "(FileManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "FileRevision", true, "build", "(URL)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "FileRevision", true, "getFile", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "JarEntryRevision", true, "build", "(URL,FileManager)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Locatable", true, "getLocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "LocatableProperties", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "getPropertyLocation", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "setProperty", "(String,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "setProperty", "(String,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "setProperty", "(String,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocatableProperties", true, "setProperty", "(String,String,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Located", true, "setLocation", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Location", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Location", true, "getURI", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes$Pipe", true, "Pipe", "(ContentHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", true, "addLocationAttributes", "(Locator,Attributes)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", true, "getLocation", "(Attributes,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", true, "getLocation", "(Element)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", true, "getLocation", "(Element,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", true, "getLocation", "(Element,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,Location)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "LocationImpl", "(String,String,int,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "get", "(Location)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationImpl", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", true, "getLocation", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", true, "getLocation", "(Object,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", true, "getLocation", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", true, "parse", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", true, "toString", "(Location)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "getCurrentPropertyPath", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "getFullPropertyPath", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "getLastBeanPropertyAccessed", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "getSetMap", "(Map,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "setFullPropertyPath", "(Map,String)", "", "Argument[1]", "Argument[0].Element", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", true, "setLastBeanPropertyAccessed", "(Map,String)", "", "Argument[1]", "Argument[0].Element", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(String,Throwable,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionException", true, "ReflectionException", "(Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", true, "getRealTarget", "(String,Map,Object)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassPathFinder", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassPathFinder", true, "setPattern", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassPathFinder", true, "setPatternMatcher", "(PatternMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "CompoundRoot", true, "peek", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "CompoundRoot", true, "pop", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "CompoundRoot", true, "push", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$DOMBuilder", true, "DOMBuilder", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$DOMBuilder", true, "DOMBuilder", "(SAXTransformerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$DOMBuilder", true, "DOMBuilder", "(SAXTransformerFactory,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$DOMBuilder", true, "DOMBuilder", "(SAXTransformerFactory,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$StartHandler", true, "StartHandler", "(ContentHandler,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$StartHandler", true, "StartHandler", "(ContentHandler,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper", true, "getLocationObject", "(Element)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "Evaluated", true, "Evaluated", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "Evaluated", true, "get", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "MemberAccessValueStack", true, "setAcceptProperties", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "MemberAccessValueStack", true, "setExcludeProperties", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "NamedVariablePatternMatcher$CompiledPattern", true, "CompiledPattern", "(Pattern,List)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "NamedVariablePatternMatcher$CompiledPattern", true, "CompiledPattern", "(Pattern,List)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "NamedVariablePatternMatcher$CompiledPattern", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "NamedVariablePatternMatcher$CompiledPattern", true, "getVariableNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", true, "getCommentLines", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", true, "getPropertyName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", true, "getPropertyValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$NameEndsWith", true, "NameEndsWith", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$NameEndsWith", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$NameIs", true, "NameIs", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$NameIs", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", true, "getClassLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", true, "getClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", true, "getResources", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", true, "setClassLoader", "(ClassLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "StrutsLocalizedTextProvider", true, "localeFromString", "(String,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil$ParsedValueEvaluator", true, "evaluate", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "commaDelimitedStringToSet", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class,ParsedValueEvaluator)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class,ParsedValueEvaluator,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char[],String,ValueStack,Class,ParsedValueEvaluator)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char[],String,ValueStack,Class,ParsedValueEvaluator,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParser", true, "evaluate", "(char[],String,ParsedValueEvaluator,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findString", "(String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findValue", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findValue", "(String,Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findValue", "(String,Class,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "findValue", "(String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "getActionContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "getContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "getExprOverrides", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "getRoot", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "peek", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "pop", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "push", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", true, "setExprOverrides", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStackFactory", true, "createValueStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStackFactory", true, "createValueStack", "(ValueStack)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "WildcardUtil", true, "compileWildcardPattern", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.util", "XWorkTestCaseHelper", true, "loadConfigurationProviders", "(ConfigurationManager,ConfigurationProvider[])", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ConditionalVisitorFieldValidator", true, "getExpression", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ConditionalVisitorFieldValidator", true, "setExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", true, "setMaxExclusiveExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", true, "setMaxInclusiveExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", true, "setMinExclusiveExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", true, "setMinInclusiveExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ExpressionValidator", true, "getExpression", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ExpressionValidator", true, "setExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "FieldExpressionValidator", true, "getExpression", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "FieldExpressionValidator", true, "setExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "FieldValidatorSupport", true, "getCurrentValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "getMax", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "getMin", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "setMax", "(Comparable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "setMaxExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "setMin", "(Comparable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RangeValidatorSupport", true, "setMinExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", true, "getRegex", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", true, "setCaseSensitiveExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", true, "setRegex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", true, "setRegexExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", true, "setTrimExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RepopulateConversionErrorFieldValidatorSupport", true, "doValidate", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", true, "setMaxLengthExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", true, "setMinLengthExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", true, "setTrimExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "URLValidator", true, "setUrlRegexExpression", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ValidatorSupport", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "AppendingValidatorContext", "(ValidatorContext,TextProvider,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "AppendingValidatorContext", "(ValidatorContext,TextProvider,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "AppendingValidatorContext", "(ValidatorContext,TextProvider,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "AppendingValidatorContext", "(ValidatorContext,TextProvider,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "getFieldNameWithField", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator$AppendingValidatorContext", true, "getFieldNameWithField", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator", true, "getContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator", true, "setActionValidatorManager", "(ActionValidatorManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator", true, "setContext", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "getValidators", "(Class,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "getValidators", "(Class,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "getValidators", "(Class,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "getValidators", "(Class,String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "validate", "(Object,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "validate", "(Object,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "validate", "(Object,String,ValidatorContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ActionValidatorManager", true, "validate", "(Object,String,ValidatorContext,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "AnnotationValidationConfigurationBuilder", true, "AnnotationValidationConfigurationBuilder", "(ValidatorFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultActionValidatorManager", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultActionValidatorManager", true, "setValidatorFactory", "(ValidatorFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultActionValidatorManager", true, "setValidatorFileParser", "(ValidatorFileParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFactory", true, "DefaultValidatorFactory", "(ObjectFactory,ValidatorFileParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFactory", true, "DefaultValidatorFactory", "(ObjectFactory,ValidatorFileParser)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFileParser", true, "getMultiTextvalueSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFileParser", true, "getTextValue", "(Element)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFileParser", true, "setMultiTextvalueSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultValidatorFileParser", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "DelegatingValidatorContext", "(Object,TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "DelegatingValidatorContext", "(Object,TextProviderFactory)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "DelegatingValidatorContext", "(ValidationAware,TextProvider,LocaleProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "DelegatingValidatorContext", "(ValidationAware,TextProvider,LocaleProvider)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "DelegatingValidatorContext", "(ValidationAware,TextProvider,LocaleProvider)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "makeTextProvider", "(Object,TextProviderFactory)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", true, "makeTextProvider", "(Object,TextProviderFactory)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "FieldValidator", true, "getFieldName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "FieldValidator", true, "setFieldName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationException", true, "ValidationException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationException", true, "ValidationException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationException", true, "ValidationException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", true, "setActionValidatorManager", "(ActionValidatorManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "Builder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "Builder", "(ValidatorConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "addParam", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "addParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "defaultMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "defaultMessage", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "location", "(Location)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "location", "(Location)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "messageKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "messageKey", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "messageParams", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "messageParams", "(String[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "removeParam", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig$Builder", false, "shortCircuit", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", true, "getDefaultMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", true, "getMessageKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", true, "getMessageParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorContext", true, "getFullFieldName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorContext", true, "getFullFieldName", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFactory", true, "getValidator", "(ValidatorConfig)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFactory", true, "lookupRegisteredValidatorType", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFactory", true, "registerValidator", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFactory", true, "registerValidator", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "ActionChainResult", "(String,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "getProxy", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "setActionProxyFactory", "(ActionProxyFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", true, "setSkipActions", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getActionInvocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getActionMapping", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getActionName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getApplication", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getContainer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getContextMap", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getConversionErrors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getLocale", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getPageContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getServletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getServletRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getServletResponse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getSession", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "getValueStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "of", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "put", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "put", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "with", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "with", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "with", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionInvocation", "(ActionInvocation)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionInvocation", "(ActionInvocation)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionMapping", "(ActionMapping)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionMapping", "(ActionMapping)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withActionName", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withApplication", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withApplication", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withContainer", "(Container)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withConversionErrors", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withConversionErrors", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withExtraContext", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withExtraContext", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withLocale", "(Locale)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withPageContext", "(PageContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withPageContext", "(PageContext)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withParameters", "(HttpParameters)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withParameters", "(HttpParameters)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletContext", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletContext", "(ServletContext)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletRequest", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletRequest", "(HttpServletRequest)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletResponse", "(HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withServletResponse", "(HttpServletResponse)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withSession", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withSession", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withValueStack", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", true, "withValueStack", "(ValueStack)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "addPreResultListener", "(PreResultListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getAction", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getInvocationContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getProxy", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getResultCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "getStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "init", "(ActionProxy)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "invoke", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "invokeActionOnly", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "setActionEventListener", "(ActionEventListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", true, "setResultCode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "execute", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getAction", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getActionName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getInvocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getMethod", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", true, "getNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(ActionInvocation,String,String,String,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(ActionInvocation,String,String,String,boolean,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(String,String,String,Map)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(String,String,String,Map)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(String,String,String,Map,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxyFactory", true, "createActionProxy", "(String,String,String,Map,boolean,boolean)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", true, "getFormatted", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", true, "getFormatted", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "AsyncManager", true, "getAsyncActionResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "CompositeTextProvider", true, "CompositeTextProvider", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "CompositeTextProvider", true, "CompositeTextProvider", "(TextProvider[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "DefaultActionInvocation", "(Map,boolean)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "createResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setAsyncManager", "(AsyncManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setUnknownHandlerManager", "(UnknownHandlerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionInvocation", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxy", true, "setActionEventListener", "(ActionEventListener)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxy", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxy", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxy", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxy", true, "setUnknownHandler", "(UnknownHandlerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(ActionInvocation,String,String,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(ActionInvocation,String,String,boolean,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(String,String,Map)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(String,String,Map)", "", "Argument[2].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(String,String,Map,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "createActionProxy", "(String,String,Map,boolean,boolean)", "", "Argument[2].Element", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultTextProvider", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultUnknownHandlerManager", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", true, "getAllPhysicalUrls", "(URL)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", true, "normalizeToFileProtocol", "(URL)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "FileManagerFactory", true, "getFileManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", true, "findDefaultText", "(String,Locale)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", true, "findResourceBundle", "(String,Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", true, "findResourceBundle", "(String,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", true, "findResourceBundle", "(String,Locale)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "buildBean", "(Class,Map)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "buildBean", "(String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "buildBean", "(String,Map)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "buildBean", "(String,Map,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "buildBean", "(String,Map,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "getClassInstance", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setActionFactory", "(ActionFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setClassLoader", "(ClassLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setConverterFactory", "(ConverterFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setInterceptorFactory", "(InterceptorFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setResultFactory", "(ResultFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setUnknownHandlerFactory", "(UnknownHandlerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", true, "setValidatorFactory", "(ValidatorFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ResourceBundleTextProvider", true, "setBundle", "(ResourceBundle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "ResourceBundleTextProvider", true, "setLocaleProvider", "(LocaleProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "Result", true, "execute", "(ActionInvocation)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "StrutsTextProviderFactory", true, "setDefaultTextProvider", "(TextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "StrutsTextProviderFactory", true, "setLocaleProviderFactory", "(LocaleProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "StrutsTextProviderFactory", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,List)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,List)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getTexts", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getTexts", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getTexts", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderFactory", true, "createInstance", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderFactory", true, "createInstance", "(ResourceBundle)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderFactory", true, "createInstance", "(ResourceBundle)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "TextProviderSupport", "(Class,LocaleProvider,LocalizedTextProvider)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "TextProviderSupport", "(Class,LocaleProvider,LocalizedTextProvider)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "TextProviderSupport", "(ResourceBundle,LocaleProvider,LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "TextProviderSupport", "(ResourceBundle,LocaleProvider,LocalizedTextProvider)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "TextProviderSupport", "(ResourceBundle,LocaleProvider,LocalizedTextProvider)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandler", true, "handleUnknownAction", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandlerManager", true, "getUnknownHandlers", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandlerManager", true, "handleUnknownAction", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandlerManager", true, "handleUnknownMethod", "(Object,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "BeanValidationInterceptor", true, "setBeanValidationManager", "(BeanValidationManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "BeanValidationInterceptor", true, "setConvertFromEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "BeanValidationInterceptor", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "DefaultBeanValidationManager", true, "DefaultBeanValidationManager", "(String,String,ObjectFactory)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.struts2.action", "CspReportAction", true, "getServletRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.action", "CspReportAction", true, "setServletRequest", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.action", "ServletContextAware", true, "withServletContext", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.action", "ServletRequestAware", true, "withServletRequest", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.action", "SessionAware", true, "withSession", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(Callable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(Executor,Callable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(Executor,Callable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(long,Callable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(long,Executor,Callable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "AsyncAction", "(long,Executor,Callable)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", true, "getExecutor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.cdi", "CdiObjectFactory", true, "setJndiKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.compiler", "MemoryClassLoader", true, "addMemoryJavaFileObject", "(String,MemoryJavaFileObject)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.compiler", "MemoryClassLoader", true, "addMemoryJavaFileObject", "(String,MemoryJavaFileObject)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.compiler", "MemoryJavaFileObject", true, "toByteArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "FreemarkerTemplateEngine", true, "setFreemarkerManager", "(FreemarkerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "JspTemplateEngine", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "Template", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "Template", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "Template", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getPossibleTemplates", "(TemplateEngine)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getPossibleTemplates", "(TemplateEngine)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getPossibleTemplates", "(TemplateEngine)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "getTheme", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "Template", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngine", true, "getThemeProps", "(Template)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngine", true, "getThemeProps", "(Template)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngine", true, "renderTemplate", "(TemplateRenderingContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngineManager", true, "registerTemplateEngine", "(String,TemplateEngine)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngineManager", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngineManager", true, "setDefaultTemplateType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "TemplateRenderingContext", "(Template,Writer,ValueStack,Map,UIBean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "TemplateRenderingContext", "(Template,Writer,ValueStack,Map,UIBean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "TemplateRenderingContext", "(Template,Writer,ValueStack,Map,UIBean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "TemplateRenderingContext", "(Template,Writer,ValueStack,Map,UIBean)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "TemplateRenderingContext", "(Template,Writer,ValueStack,Map,UIBean)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "getStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "getTag", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "getTemplate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateRenderingContext", true, "getWriter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "ActionComponent", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "ActionComponent", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "ActionComponent", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "getProxy", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "setActionProxyFactory", "(ActionProxyFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionError", true, "ActionError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionError", true, "ActionError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionError", true, "ActionError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionMessage", true, "ActionMessage", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionMessage", true, "ActionMessage", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ActionMessage", true, "ActionMessage", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "Anchor", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "Anchor", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "Anchor", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "getUrlProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setExtraParameterProvider", "(ExtraParameterProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setHref", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setPortletUrlType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setUrlIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setUrlRenderer", "(UrlRenderer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Anchor", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "AppendIterator", true, "AppendIterator", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Bean", true, "Bean", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Bean", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Bean", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Bean", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "Checkbox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "Checkbox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "Checkbox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "setFieldValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "setSubmitUnchecked", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Checkbox", true, "setSubmitUncheckedGlobal", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "CheckboxList", true, "CheckboxList", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "CheckboxList", true, "CheckboxList", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "CheckboxList", true, "CheckboxList", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ClosingUIBean", true, "setOpenTemplate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "ComboBox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "ComboBox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "ComboBox", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComboBox", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "Component", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "addAllParameters", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "addParameter", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "addParameter", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "copyParams", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "end", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "end", "(Writer,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "end", "(Writer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "findString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "getComponentStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "getStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "setNotExcludedAcceptedPatterns", "(NotExcludedAcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "setUrlHelper", "(UrlHelper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Component", true, "start", "(Writer)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComponentUrlProvider", true, "ComponentUrlProvider", "(Component,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComponentUrlProvider", true, "ComponentUrlProvider", "(Component,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ComponentUrlProvider", true, "getUrlRenderer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "ContextBean", true, "ContextBean", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "Date", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "formatTime", "(TextProvider,ZonedDateTime)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "getFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "getTimezone", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "setDateFormatter", "(DateFormatter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "setFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Date", true, "setTimezone", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DateTextField", true, "DateTextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DateTextField", true, "DateTextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DateTextField", true, "DateTextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DateTextField", true, "setFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Debug", true, "Debug", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Debug", true, "Debug", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Debug", true, "Debug", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Debug", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "DoubleListUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "DoubleListUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "DoubleListUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleDisabled", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleEmptyOption", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleHeaderKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleHeaderValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleListKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleListValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleMultiple", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnblur", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnchange", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOndblclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnfocus", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnkeydown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnkeypress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnkeyup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnmousedown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnmousemove", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnmouseout", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnmouseover", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnmouseup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleOnselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleSize", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getDoubleValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "getFormName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleAccesskey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnblur", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnchange", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOndblclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnfocus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnkeydown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnkeypress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnkeyup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnmousedown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnmousemove", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnmouseout", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnmouseover", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnmouseup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleOnselect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setDoubleValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setFormName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleListUIBean", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleSelect", true, "DoubleSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleSelect", true, "DoubleSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "DoubleSelect", true, "DoubleSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Else", true, "Else", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ElseIf", true, "ElseIf", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ElseIf", true, "setTest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FieldError", true, "FieldError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FieldError", true, "FieldError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FieldError", true, "FieldError", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FieldError", true, "getFieldErrorFieldNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "File", true, "File", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "File", true, "File", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "File", true, "File", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "File", true, "setAccept", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "File", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "FieldVisitorValidatorWrapper", "(FieldValidator,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "FieldVisitorValidatorWrapper", "(FieldValidator,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "getFieldValidator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "getNamePrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "setFieldValidator", "(FieldValidator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form$FieldVisitorValidatorWrapper", true, "setNamePrefix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "Form", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "Form", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "Form", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setAcceptcharset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setActionValidatorManager", "(ActionValidatorManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setEnctype", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setFocusElement", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setOnreset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setOnsubmit", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setTarget", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setUrlRenderer", "(UrlRenderer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setValidate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Form", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "FormButton", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "FormButton", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "FormButton", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "FormButton", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "GenericUIBean", true, "GenericUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "GenericUIBean", true, "GenericUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "GenericUIBean", true, "GenericUIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Head", true, "Head", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Head", true, "Head", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Head", true, "Head", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Head", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Hidden", true, "Hidden", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Hidden", true, "Hidden", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Hidden", true, "Hidden", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "I18n", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "setLocaleProviderFactory", "(LocaleProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "setLocalizedTextProvider", "(LocalizedTextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "setTextProvider", "(TextProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "I18n", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "If", true, "If", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "If", true, "setTest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "Include", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "Include", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "Include", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "getContextRelativePath", "(ServletRequest,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "getContextRelativePath", "(ServletRequest,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "include", "(String,Writer,ServletRequest,HttpServletResponse,String)", "", "Argument[0]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Include", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "InputTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "InputTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "InputTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getAddLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getAllowRemoveAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getAllowUpDown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getButtonCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getButtonCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getHeaderKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getHeaderValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getLeftTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getMultiple", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getRemoveAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getRemoveLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getRightTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getSize", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "getUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setAddLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setAllowRemoveAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setAllowUpDown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setButtonCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setButtonCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setLeftTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setRemoveAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setRemoveLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setRightTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "InputTransferSelect", true, "setUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "IteratorComponent", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "setBegin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "setEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "setStatus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "setStep", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "IteratorComponent", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Label", true, "Label", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Label", true, "Label", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Label", true, "Label", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Label", true, "setFor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "Link", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "Link", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "Link", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setAs", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setCrossorigin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setHref", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setHreflang", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setMedia", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setReferrerpolicy", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setRel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setSizes", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Link", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setList", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListLabelKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", true, "setListValueKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "MergeIterator", true, "MergeIterator", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "Number", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "getCurrency", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "getRoundingMode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "setCurrency", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "setRoundingMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Number", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "OptGroup", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "OptGroup", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "OptGroup", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setList", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "OptionTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "OptionTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "OptionTransferSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddAllToLeftLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddAllToLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddAllToRightLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddAllToRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddToLeftLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddToLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddToRightLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAddToRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowAddAllToLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowAddAllToRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowAddToLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowAddToRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowSelectAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowUpDownOnLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getAllowUpDownOnRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getButtonCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getButtonCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getLeftDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getLeftTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getLeftUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getRightDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getRightTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getRightUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getSelectAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getSelectAllOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getUpDownOnLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "getUpDownOnRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddAllToLeftLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddAllToLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddAllToRightLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddAllToRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddToLeftLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddToLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddToRightLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAddToRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowAddAllToLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowAddAllToRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowAddToLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowAddToRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowSelectAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowUpDownOnLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setAllowUpDownOnRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setButtonCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setButtonCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setLeftDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setLeftTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setLeftUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setRightDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setRightTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setRightUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setSelectAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setSelectAllOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setUpDownOnLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "OptionTransferSelect", true, "setUpDownOnRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Param$UnnamedParametric", true, "addParameter", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Param", true, "Param", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Param", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Param", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Password", true, "Password", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Password", true, "Password", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Password", true, "Password", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Password", true, "setShowPassword", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "PortletUrlRenderer", true, "setQueryStringParser", "(QueryStringParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "PortletUrlRenderer", true, "setUrlHelper", "(UrlHelper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Property", true, "Property", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Property", true, "setDefault", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Property", true, "setDefaultValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Property", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Push", true, "Push", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Push", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Radio", true, "Radio", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Radio", true, "Radio", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Radio", true, "Radio", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Reset", true, "Reset", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Reset", true, "Reset", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Reset", true, "Reset", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Reset", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "Script", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "Script", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "Script", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setAsync", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setCharset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setCrossorigin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setDefer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setIntegrity", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setNomodule", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setReferrerpolicy", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Script", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "Select", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "Select", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "Select", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Select", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ServletUrlRenderer", true, "setQueryStringParser", "(QueryStringParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "ServletUrlRenderer", true, "setUrlHelper", "(UrlHelper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Set", true, "Set", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Set", true, "setScope", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Set", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Submit", true, "Submit", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Submit", true, "Submit", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Submit", true, "Submit", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Submit", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Text", true, "Text", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Text", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "TextArea", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "TextArea", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "TextArea", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setCols", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setMaxlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setMinlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setReadonly", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setRows", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextArea", true, "setWrap", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "TextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "TextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "TextField", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "setMaxLength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "setMaxlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "setReadonly", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "TextField", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Token", true, "Token", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Token", true, "Token", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "Token", true, "Token", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "UIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "UIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "UIBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "getId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "getTemplate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "getTemplateDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "getTheme", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setAccesskey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setCssErrorClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setCssErrorStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setDefaultTemplateDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setDefaultUITheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setDynamicAttributes", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setErrorPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setJavascriptTooltip", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setLabelPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setLabelSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnblur", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnchange", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOndblclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnfocus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnkeydown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnkeypress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnkeyup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnmousedown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnmousemove", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnmouseout", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnmouseover", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnmouseup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setOnselect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setRequiredLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setRequiredPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setStaticContentPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTabindex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTemplate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTemplateDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTemplateEngineManager", "(TemplateEngineManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTooltip", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTooltipConfig", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTooltipCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTooltipDelay", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setTooltipIconPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setUIThemeExpansionToken", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UIBean", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "URL", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "URL", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "URL", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "getUrlProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setExtraParameterProvider", "(ExtraParameterProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setPortletUrlType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setUrlIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setUrlRenderer", "(UrlRenderer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "URL", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "UpDownSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "UpDownSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "UpDownSelect", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getAllowMoveDown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getAllowMoveUp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getAllowSelectAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getMoveDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getMoveUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "getSelectAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setAllowMoveDown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setAllowMoveUp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setAllowSelectAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setMoveDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setMoveUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UpDownSelect", true, "setSelectAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "determineActionURL", "(String,String,String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "determineActionURL", "(String,String,String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[5].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "determineActionURL", "(String,String,String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[6]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getAction", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getAnchor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getExtraParameterProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getHttpServletRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getHttpServletResponse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getIncludeParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getMethod", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getPortletMode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getPortletUrlType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getScheme", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getUrlIncludeParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getVar", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "getWindowState", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setExtraParameterProvider", "(ExtraParameterProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setHttpServletRequest", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setHttpServletResponse", "(HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setPortletUrlType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setUrlIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setUrlRenderer", "(UrlRenderer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlRenderer", true, "renderUrl", "(Writer,UrlProvider)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.components", "UrlRenderer", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", true, "BeanConfig", "(Class,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", true, "BeanConfig", "(Class,String,Class)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", true, "BeanConfig", "(Class,String,Class,Scope,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanSelectionConfig", true, "BeanSelectionConfig", "(Class,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanSelectionConfig", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAcceptedPatternsChecker", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getActionExcludePattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getActionExtension", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getActionProxyFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getActionValidatorManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAdditionalAcceptedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAdditionalExcludedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAllAsStringsMap", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAllowedActionNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getAllowedMethodNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getBeaninfoCacheFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getContentTypeMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterAnnotationProcessor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterCreator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterFileProcessor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterHolder", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterNumber", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterPropertiesProcessor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getConverterString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getCustomI18nResources", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getCustomProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDefaultActionName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDefaultMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDevModeExcludedClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDevModeExcludedPackageExemptClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDevModeExcludedPackageNamePatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDevModeExcludedPackageNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getDispatcherErrorHandler", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExcludedClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExcludedPackageExemptClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExcludedPackageNamePatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExcludedPackageNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExcludedPatternsChecker", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExpressionCacheFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getExpressionParser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getFreemarkerManagerClassname", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getFreemarkerTemplatesCacheUpdateDelay", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getI18nEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getLocale", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getLocaleProviderFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getLocalizedTextProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMapperClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMapperComposite", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMapperIdParameterName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMapperPrefixMapping", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMultipartParser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMultipartSaveDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getMultipartValidationRegex", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getNotExcludedAcceptedPatternsChecker", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryActionFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryConverterFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryInterceptorFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryResultFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactorySpringAutoWire", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryUnknownHandlerFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectFactoryValidatorFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getObjectTypeDeterminer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getOverrideAcceptedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getOverrideExcludedPatterns", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getPatternMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getReflectionContextFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getReflectionProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getStaticContentLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getStaticContentPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getStrictMethodInvocationMethodRegex", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getTextProviderFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUiTemplateDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUiTemplateSuffix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUiTheme", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUiThemeExpansionToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUnknownHandlerManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUrlIncludeParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getUrlRenderer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getValueStackFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getVelocityConfigfile", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getVelocityContexts", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getVelocityManagerClassname", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getVelocityToolboxlocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getViewUrlHelper", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "getXworkConverter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setAcceptedPatternsChecker", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setActionExcludePattern", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setActionExtension", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setActionProxyFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setActionValidatorManager", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setAdditionalAcceptedPatterns", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setAdditionalExcludedPatterns", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setAllowedActionNames", "(Pattern)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setAllowedMethodNames", "(Pattern)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setBeaninfoCacheFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setContentTypeMatcher", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterAnnotationProcessor", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterArray", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterCollection", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterCreator", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterDate", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterFileProcessor", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterHolder", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterNumber", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterPropertiesProcessor", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setConverterString", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setCustomI18nResources", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setCustomProperties", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDefaultActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDefaultMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDevModeExcludedClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDevModeExcludedPackageExemptClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDevModeExcludedPackageNamePatterns", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDevModeExcludedPackageNames", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setDispatcherErrorHandler", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExcludedClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExcludedPackageExemptClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExcludedPackageNamePatterns", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExcludedPackageNames", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExcludedPatternsChecker", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExpressionCacheFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setExpressionParser", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setFreemarkerManagerClassname", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setFreemarkerTemplatesCacheUpdateDelay", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setI18nEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setLocaleProviderFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setLocalizedTextProvider", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMapperClass", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMapperComposite", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMapperIdParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMapperPrefixMapping", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMultipartParser", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMultipartSaveDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setMultipartValidationRegex", "(Pattern)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setNotExcludedAcceptedPatternsChecker", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryActionFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryConverterFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryInterceptorFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryResultFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactorySpringAutoWire", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryUnknownHandlerFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectFactoryValidatorFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setObjectTypeDeterminer", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setOverrideAcceptedPatterns", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setOverrideExcludedPatterns", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setPatternMatcher", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setReflectionContextFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setReflectionProvider", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setStaticContentLoader", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setStaticContentPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setStrictMethodInvocationMethodRegex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setTextProviderFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUiTemplateDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUiTemplateSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUiTheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUiThemeExpansionToken", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUnknownHandlerManager", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUrlIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setUrlRenderer", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setValueStackFactory", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setVelocityConfigfile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setVelocityContexts", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setVelocityManagerClassname", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setVelocityToolboxlocation", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setViewUrlHelper", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", true, "setXworkConverter", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "Settings", true, "get", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsJavaConfigurationProvider", true, "StrutsJavaConfigurationProvider", "(StrutsJavaConfiguration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsJavaConfigurationProvider", true, "setValueSubstitutor", "(ValueSubstitutor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "StrutsXmlConfigurationProvider", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "StrutsXmlConfigurationProvider", "(String,ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "StrutsXmlConfigurationProvider", "(String,ServletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "StrutsXmlConfigurationProvider", "(String,boolean,ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "StrutsXmlConfigurationProvider", "(String,boolean,ServletContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "getActionNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "getExtension", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "getNamespaces", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "setConfigurationHelper", "(ConfigurationHelper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "setExtension", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ConfigurationHelper", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "getClazz", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "getContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "getValidators", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "setActionValidatorManager", "(ActionValidatorManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "setClazz", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "setContext", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", true, "stripPackage", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "Binding", "(String,String,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "Binding", "(String,String,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "Binding", "(String,String,String,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "getAlias", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "getConstant", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", true, "getImpl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction", true, "getBeans", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "getActionName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "getConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "getDetailView", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "getProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "setDetailView", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConstantsAction", true, "getConstants", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "PropertyInfo", "(String,Class,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "PropertyInfo", "(String,Class,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", true, "setValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", true, "getProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", true, "getSelectedValidator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", true, "setReflectionContextFactory", "(ReflectionContextFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionConfigBuilder", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionFileProtocols", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionIncludeJars", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionNameBuilder", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionNameSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionPackages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionActionSuffix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionConventionsService", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionDefaultParentPackage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionExcludePackages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionInterceptorMapBuilder", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionPackageLocators", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionPackageLocatorsBasePackage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionRelativeResultTypes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionResultMapBuilder", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "getConventionResultPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionConfigBuilder", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionFileProtocols", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionIncludeJars", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionNameBuilder", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionNameSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionPackages", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionActionSuffix", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionConventionsService", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionDefaultParentPackage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionExcludePackages", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionInterceptorMapBuilder", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionPackageLocators", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionPackageLocatorsBasePackage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionRelativeResultTypes", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionResultMapBuilder", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", true, "setConventionResultPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "AbstractActionNameBuilder", true, "setActionSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ActionNameBuilder", true, "build", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ActionNameBuilder", true, "build", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler$Resource", true, "Resource", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler$Resource", true, "Resource", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler", true, "ConventionUnknownHandler", "(Configuration,ObjectFactory,ServletContext,Container,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler", true, "ConventionUnknownHandler", "(Configuration,ObjectFactory,ServletContext,Container,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler", true, "ConventionUnknownHandler", "(Configuration,ObjectFactory,ServletContext,Container,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler", true, "ConventionUnknownHandler", "(Configuration,ObjectFactory,ServletContext,Container,String,String,String)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionUnknownHandler", true, "ConventionUnknownHandler", "(Configuration,ObjectFactory,ServletContext,Container,String,String,String)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionsService", true, "determineResultPath", "(ActionConfig)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionsService", true, "determineResultPath", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionsService", true, "getResultTypesByExtension", "(PackageConfig)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ConventionsServiceImpl", true, "ConventionsServiceImpl", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultClassFinder$InfoBuildingMethodVisitor", true, "InfoBuildingMethodVisitor", "(Info)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultClassFinder$InfoBuildingVisitor", true, "InfoBuildingVisitor", "(ClassFinder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultClassFinder", true, "DefaultClassFinder", "(ClassLoaderInterface,Collection,boolean,Set,Test)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultInterceptorMapBuilder", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultResultMapBuilder", true, "DefaultResultMapBuilder", "(ServletContext,Container,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "DefaultResultMapBuilder", true, "DefaultResultMapBuilder", "(ServletContext,Container,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "PackageBasedActionConfigBuilder", "(Configuration,Container,ObjectFactory,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "PackageBasedActionConfigBuilder", "(Configuration,Container,ObjectFactory,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "PackageBasedActionConfigBuilder", "(Configuration,Container,ObjectFactory,String,String,String)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setActionPackages", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setActionSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setClassFinderFactory", "(ClassFinderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setExcludePackages", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setFileProtocols", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setIncludeJars", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setPackageLocators", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", true, "setPackageLocatorsBase", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ResultMapBuilder", true, "build", "(Class,Action,String,PackageConfig)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ResultMapBuilder", true, "build", "(Class,Action,String,PackageConfig)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "ResultMapBuilder", true, "build", "(Class,Action,String,PackageConfig)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.convention", "SEOActionNameBuilder", true, "SEOActionNameBuilder", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.convention", "StringTools", true, "createParameterMap", "(String[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "StrutsConversionPropertiesProcessor", true, "setTypeConverterCreator", "(TypeConverterCreator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "StrutsConversionPropertiesProcessor", true, "setTypeConverterHolder", "(TypeConverterHolder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "StrutsTypeConverterCreator", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "TypeConversionException", true, "TypeConversionException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "TypeConversionException", true, "TypeConversionException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "TypeConversionException", true, "TypeConversionException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.conversion", "TypeConversionException", true, "TypeConversionException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.filter", "FilterHostConfig", true, "FilterHostConfig", "(FilterConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.listener", "ListenerHostConfig", true, "ListenerHostConfig", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapper", true, "getMapping", "(HttpServletRequest,ConfigurationManager)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapper", true, "getMapping", "(HttpServletRequest,ConfigurationManager)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapper", true, "getMappingFromActionName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapper", true, "getUriFromActionMapping", "(ActionMapping)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapper", true, "getUriFromActionMapping", "(ActionMapping)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "ActionMapping", "(Result)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "ActionMapping", "(String,String,String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "ActionMapping", "(String,String,String,Map)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "ActionMapping", "(String,String,String,Map)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "ActionMapping", "(String,String,String,Map)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getExtension", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getMethod", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "getResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setExtension", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setParams", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "setResult", "(Result)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "ActionMapping", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "handleSpecialParameters", "(HttpServletRequest,ActionMapping)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setAllowedActionNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setAllowedMethodNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setAllowedNamespaceNames", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setDefaultActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setDefaultMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setDefaultNamespaceName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", true, "setExtensions", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "PrefixBasedActionMapper", true, "setPrefixBasedActionMappers", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "Restful2ActionMapper", true, "getIdParameterName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "Restful2ActionMapper", true, "setDecoder", "(UrlDecoder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "Restful2ActionMapper", true, "setIdParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "RestfulActionMapper", true, "setDecoder", "(UrlDecoder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "FileInfo", "(File,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "FileInfo", "(File,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "FileInfo", "(File,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "getFile", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "JakartaStreamMultiPartRequest$FileInfo", true, "getOriginalName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getContentType", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getErrors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getFile", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getFileNames", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getFileParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getFilesystemName", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getParameter", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "getParameterValues", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", true, "parse", "(HttpServletRequest,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider,boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "MultiPartRequestWrapper", "(MultiPartRequest,HttpServletRequest,String,LocaleProvider,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "getErrors", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", true, "getFileParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "StrutsUploadedFile", true, "StrutsUploadedFile", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", true, "getAbsolutePath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", true, "getContent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher.servlet", "ServletHostConfig", true, "ServletHostConfig", "(ServletConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ApplicationMap", true, "ApplicationMap", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ApplicationMap", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ApplicationMap", true, "remove", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "ChartResult", "(JFreeChart,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "ChartResult", "(JFreeChart,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "ChartResult", "(JFreeChart,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "getChart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "getHeight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "getWidth", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "setChart", "(JFreeChart)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "setHeight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ChartResult", true, "setWidth", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultActionSupport", true, "getSuccessResultValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultActionSupport", true, "setSuccessResultValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultDispatcherErrorHandler", true, "setFreemarkerManager", "(FreemarkerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultStaticContentLoader", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultStaticContentLoader", true, "setStaticContentPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher$Locator", true, "getLocation", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "Dispatcher", "(ServletContext,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "Dispatcher", "(ServletContext,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[1].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[2].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[5]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "createContextMap", "(Map,HttpParameters,Map,Map,HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "getConfigurationManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "getInstance", "(ServletContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "prepare", "(HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "prepare", "(HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "prepare", "(HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "sendError", "(HttpServletRequest,HttpServletResponse,int,Exception)", "", "Argument[3]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setDefaultLocale", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setDispatcherErrorHandler", "(DispatcherErrorHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setMultipartHandler", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setMultipartSaveDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setMultipartValidationRegex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", true, "wrapRequest", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherErrorHandler", true, "handleError", "(HttpServletRequest,HttpServletResponse,int,Exception)", "", "Argument[3]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherErrorHandler", true, "init", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherErrorHandler", true, "init", "(ServletContext)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "ExecuteOperations", true, "ExecuteOperations", "(Dispatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HostConfig", true, "getInitParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HostConfig", true, "getServletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "buildNoNestedWrapping", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "withComparator", "(Comparator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "withExtraParams", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "withExtraParams", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "withParent", "(HttpParameters)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters$Builder", true, "withParent", "(HttpParameters)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", true, "appendAll", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", true, "appendAll", "(Map)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", true, "create", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", true, "remove", "(Set)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", true, "remove", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "InitOperations", true, "initDispatcher", "(HostConfig)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "LocalizedMessage", "(Class,String,String,Object[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "LocalizedMessage", "(Class,String,String,Object[])", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "LocalizedMessage", "(Class,String,String,Object[])", "", "Argument[3].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "getArgs", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "getDefaultMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", true, "getTextKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "MockDispatcher", true, "MockDispatcher", "(ServletContext,Map,ConfigurationManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "MockDispatcher", true, "MockDispatcher", "(ServletContext,Map,ConfigurationManager)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "MockDispatcher", true, "MockDispatcher", "(ServletContext,Map,ConfigurationManager)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$Empty", true, "Empty", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$Empty", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$File", true, "File", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$File", true, "File", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$File", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$Request", true, "Request", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$Request", true, "Request", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", true, "getMultipleValues", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", true, "getObject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "PrepareOperations", "(Dispatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "decrementRecursionCounter", "(HttpServletRequest,String,Runnable)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "findActionMapping", "(HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "findActionMapping", "(HttpServletRequest,HttpServletResponse,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "incrementRecursionCounter", "(HttpServletRequest,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "setEncodingAndLocale", "(HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "setEncodingAndLocale", "(HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "setEncodingAndLocale", "(HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", true, "wrapRequest", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "RequestMap", true, "RequestMap", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "RequestMap", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "RequestMap", true, "remove", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "SessionMap", true, "SessionMap", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.dispatcher", "StaticContentLoader$Validator", true, "validateStaticContentPath", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", true, "coerceToEnum", "(Object,Class)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", true, "coerceToString", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", true, "coerceToType", "(Object,Class)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "EvaluationContext", false, "EvaluationContext", "(ELContext,FunctionMapper,VariableMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "EvaluationContext", false, "EvaluationContext", "(ELContext,FunctionMapper,VariableMapper)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "EvaluationContext", false, "EvaluationContext", "(ELContext,FunctionMapper,VariableMapper)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "EvaluationContext", false, "getELContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", false, "ExpressionBuilder", "(String,ELContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", false, "ExpressionBuilder", "(String,ELContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", false, "createMethodExpression", "(Class,Class[])", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", false, "createMethodExpression", "(Class,Class[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", false, "createValueExpression", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperFactory", true, "FunctionMapperFactory", "(FunctionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperFactory", true, "create", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl$Function", true, "Function", "(String,String,Method)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl$Function", true, "Function", "(String,String,Method)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl$Function", true, "Function", "(String,String,Method)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl$Function", true, "getMethod", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl", true, "addFunction", "(String,String,Method)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl", true, "addFunction", "(String,String,Method)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "VariableMapperFactory", true, "VariableMapperFactory", "(VariableMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.lang", "VariableMapperFactory", true, "create", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "getLocalName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "getOutputName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "getPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "setLocalName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "setPrefix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "AstString", false, "getString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "CompositeExpression", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ELParser", "(ELParserTokenManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ELParser", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ELParser", "(InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ELParser", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ReInit", "(ELParserTokenManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ReInit", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ReInit", "(InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "ReInit", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "generateParseException", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "getNextToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", true, "getToken", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "ELParserTokenManager", "(SimpleCharStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "ELParserTokenManager", "(SimpleCharStream,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "ReInit", "(SimpleCharStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "ReInit", "(SimpleCharStream,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "getNextToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", true, "setDebugStream", "(PrintStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "closeNodeScope", "(Node,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "closeNodeScope", "(Node,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "closeNodeScope", "(Node,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "closeNodeScope", "(Node,int)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "peekNode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "popNode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "pushNode", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", true, "rootNode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "getImage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "getValue", "(EvaluationContext)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "jjtAddChild", "(Node,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "jjtGetChild", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "jjtGetParent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", true, "jjtSetParent", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ParseException", true, "ParseException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ParseException", true, "ParseException", "(Token,int[][],String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "ParseException", true, "ParseException", "(Token,int[][],String[])", "", "Argument[2].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "GetImage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "GetSuffix", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream,String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream,String,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(InputStream,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(Reader,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "ReInit", "(Reader,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream,String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream,String,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(InputStream,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(Reader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(Reader,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", true, "SimpleCharStream", "(Reader,int,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", true, "setImage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", true, "toString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", true, "toString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", true, "Token", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", true, "newToken", "(int,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.util", "ConcurrentCache", false, "get", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.util", "ConcurrentCache", false, "get", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el.util", "ConcurrentCache", false, "put", "(Object,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el.util", "ConcurrentCache", false, "put", "(Object,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionImpl", false, "MethodExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class,Class[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionImpl", false, "MethodExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class,Class[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionImpl", false, "MethodExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class,Class[])", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionImpl", false, "MethodExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class,Class[])", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionImpl", false, "MethodExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class,Class[])", "", "Argument[5].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionLiteral", true, "MethodExpressionLiteral", "(String,Class,Class[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "MethodExpressionLiteral", true, "MethodExpressionLiteral", "(String,Class,Class[])", "", "Argument[2].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionImpl", false, "ValueExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionImpl", false, "ValueExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionImpl", false, "ValueExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionImpl", false, "ValueExpressionImpl", "(String,Node,FunctionMapper,VariableMapper,Class)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionImpl", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionLiteral", false, "ValueExpressionLiteral", "(Object,Class)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.factory", "PrefixBasedActionProxyFactory", true, "setPrefixBasedActionProxyFactories", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.factory", "StrutsActionProxy", true, "StrutsActionProxy", "(ActionInvocation,String,String,String,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.factory", "StrutsActionProxy", true, "StrutsActionProxy", "(ActionInvocation,String,String,String,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.factory", "StrutsResultFactory", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.factory", "StrutsResultFactory", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspInterceptor", false, "setReportUri", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspSettings", true, "setReportUri", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "DefaultCspSettings", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "DebuggingInterceptor", true, "setFreemarkerManager", "(FreemarkerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "DebuggingInterceptor", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,char[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,char[],String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "PrettyPrintWriter", "(Writer,char[],String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "addAttribute", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", true, "startNode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", true, "getAction", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", true, "getException", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", true, "getInvocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", true, "getResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", true, "prepare", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "StrutsBackgroundProcess", true, "StrutsBackgroundProcess", "(ActionInvocation,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "StrutsBackgroundProcess", true, "StrutsBackgroundProcess", "(ActionInvocation,String,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "StrutsBackgroundProcess", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.httpmethod", "HttpMethodInterceptor", true, "setBadRequestResultName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor.servlet", "ServletPrincipalProxy", true, "ServletPrincipalProxy", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CheckboxInterceptor", true, "setUncheckedValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CoepInterceptor", true, "setExemptedPaths", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CookieInterceptor", true, "setAcceptedPatternsChecker", "(AcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CookieInterceptor", true, "setCookiesName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CookieInterceptor", true, "setCookiesValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CookieInterceptor", true, "setExcludedPatternsChecker", "(ExcludedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CoopInterceptor", true, "setExemptedPaths", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "CoopInterceptor", true, "setMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", true, "setExecutorProvider", "(ExecutorProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "FetchMetadataInterceptor", true, "setExemptedPaths", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "FileUploadInterceptor", true, "setAllowedExtensions", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "FileUploadInterceptor", true, "setAllowedTypes", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "FileUploadInterceptor", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "FileUploadInterceptor", true, "setMatcher", "(ContentTypeMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", true, "setAttributeName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", true, "setLocaleProviderFactory", "(LocaleProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", true, "setParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", true, "setRequestCookieParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", true, "setRequestOnlyParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", true, "getOperationModel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", true, "getRequestParameterSwitch", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", true, "setOperationMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", true, "setRequestParameterSwitch", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStorePreResultListener", true, "init", "(MessageStoreInterceptor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", true, "getRemoteUser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", true, "getUserPrincipal", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "RolesInterceptor", true, "setAllowedRoles", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "RolesInterceptor", true, "setDisallowedRoles", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "getSessionReset", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "setApplication", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "setKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "setSession", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "setSessionReset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.interceptor", "TokenInterceptor", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", true, "getPluginAttribute", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", true, "setPluginAttribute", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", true, "setPluginAttribute", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", true, "BeanRepository", "(ClassLoader,ErrorDispatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", true, "BeanRepository", "(ClassLoader,ErrorDispatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", true, "addBean", "(UseBean,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", true, "addBean", "(UseBean,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "getCompilationContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "getErrorDispatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "getPageInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "getPageNodes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "init", "(JspCompilationContext,JspServletWrapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", true, "init", "(JspCompilationContext,JspServletWrapper)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$ELText", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getFunctionInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "getUri", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "setFunctionInfo", "(FunctionInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "setMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "setParameters", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Function", true, "setUri", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", true, "add", "(ELNode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", true, "getMapName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", true, "iterator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", true, "setMapName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", true, "visit", "(Visitor)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Root", true, "getExpression", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Text", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Visitor", true, "visit", "(Function)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Visitor", true, "visit", "(Root)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode", true, "accept", "(Visitor)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELParser", true, "ELParser", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", true, "createJavacError", "(String,Nodes,StringBuffer,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", true, "createJavacError", "(String,Nodes,StringBuffer,int)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", true, "createJavacError", "(String,Nodes,StringBuffer,int,JspCompilationContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", true, "createJavacError", "(String,Nodes,StringBuffer,int,JspCompilationContext)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", true, "parseJavacErrors", "(String,String,Nodes)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer,JspCompilationContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer,JspCompilationContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,String,int,StringBuffer,JspCompilationContext)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "JavacErrorDetail", "(String,int,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "getErrorMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "getJavaFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "getJspExtract", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", true, "getJspFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[4].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[5].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "JspProperty", "(String,String,String,String,Vector,Vector,String,String)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "getIncludeCoda", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "getIncludePrelude", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "getPageEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "isDeferedSyntaxAllowedAsLiteral", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "isELIgnored", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "isScriptingInvalid", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "isTrimDirectiveWhitespaces", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig$JspProperty", true, "isXml", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig", true, "JspConfig", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig", true, "findJspProperty", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "JspRuntimeContext", "(ServletContext,Options)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "JspRuntimeContext", "(ServletContext,Options)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "addWrapper", "(String,JspServletWrapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "addWrapper", "(String,JspServletWrapper)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "getClassPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "getCodeSource", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "getParentClassLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "getPermissionCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", false, "getWrapper", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil$ValidAttribute", true, "ValidAttribute", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil$ValidAttribute", true, "ValidAttribute", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil$ValidAttribute", true, "ValidAttribute", "(String,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToBoolean", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToByte", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToChar", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToCharacter", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToDouble", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToFloat", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToInt", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToInteger", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToLong", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveBoolean", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveByte", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveDouble", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveFloat", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveLong", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToPrimitiveShort", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "coerceToShort", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "escapeQuotes", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "getExpr", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "interpreterCall", "(boolean,String,Class,String,boolean)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "makeXmlJavaIdentifier", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "replace", "(String,char,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "replace", "(String,char,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "toJavaSourceType", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", true, "toJavaSourceTypeFromTld", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String,Object[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String,String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Localizer", true, "getMessage", "(String,String,String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeDirective", true, "AttributeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeGenerator", true, "AttributeGenerator", "(Mark,String,CustomTag)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeGenerator", true, "AttributeGenerator", "(Mark,String,CustomTag)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeGenerator", true, "AttributeGenerator", "(Mark,String,CustomTag)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeGenerator", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$AttributeGenerator", true, "getTag", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Comment", true, "Comment", "(String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Comment", true, "Comment", "(String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Comment", true, "Comment", "(String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[8]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagFileInfo)", "", "Argument[9]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[8]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Attributes,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[9]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagFileInfo)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "CustomTag", "(String,String,String,String,Attributes,Mark,Node,TagInfo,Class)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getAtETag", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getAtSTag", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getChildInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getCustomTagParent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getJspAttributes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getScriptingVars", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getTagData", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getTagFileInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getTagHandlerPoolName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getTagInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getTagPluginContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getURI", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "getVariableInfos", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setAtETag", "(Nodes)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setAtSTag", "(Nodes)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setCustomTagParent", "(CustomTag)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setJspAttributes", "(JspAttribute[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setScriptingVars", "(Vector,int)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setTagData", "(TagData)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setTagHandlerPoolName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", true, "setTagPluginContext", "(TagPluginContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Declaration", true, "Declaration", "(String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$DoBodyAction", true, "DoBodyAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", true, "ELExpression", "(char,String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", true, "ELExpression", "(char,String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", true, "ELExpression", "(char,String,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", true, "getEL", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", true, "setEL", "(Nodes)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Expression", true, "Expression", "(String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$FallBackAction", true, "FallBackAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "ForwardAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "getPage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ForwardAction", true, "setPage", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$GetProperty", true, "GetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "IncludeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "getPage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeAction", true, "setPage", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$IncludeDirective", true, "IncludeDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$InvokeAction", true, "InvokeAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getEL", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getLocalName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getNamedAttributeNode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getTagAttributeInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getURI", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "JspBody", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspBody", true, "getChildInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "JspElement", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "getJspAttributes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "getNameAttribute", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "setJspAttributes", "(JspAttribute[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspElement", true, "setNameAttribute", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspOutput", true, "JspOutput", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspRoot", true, "JspRoot", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspText", true, "JspText", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspText", true, "JspText", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspText", true, "JspText", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspText", true, "JspText", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspText", true, "JspText", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "NamedAttribute", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "getChildInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "getPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", true, "getTemporaryVariableName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", true, "Nodes", "(Root)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", true, "add", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", true, "getNode", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", true, "getRoot", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", true, "visit", "(Visitor)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "PageDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "addImport", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PageDirective", true, "getImports", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "ParamAction", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamAction", true, "setValue", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ParamsAction", true, "ParamsAction", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "PlugIn", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "getHeight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "getWidth", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "setHeight", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$PlugIn", true, "setWidth", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", true, "getJspConfigPageEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", true, "getPageEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", true, "getParentRoot", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", true, "setJspConfigPageEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", true, "setPageEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,String,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ScriptingElement", true, "ScriptingElement", "(String,String,String,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Scriptlet", true, "Scriptlet", "(String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "SetProperty", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$SetProperty", true, "setValue", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "TagDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "addImport", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TagDirective", true, "getImports", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TaglibDirective", true, "TaglibDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TaglibDirective", true, "TaglibDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TaglibDirective", true, "TaglibDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", true, "TemplateText", "(String,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", true, "TemplateText", "(String,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", true, "TemplateText", "(String,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", true, "getExtraSmap", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", true, "setText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "UninterpretedTag", "(String,String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "getJspAttributes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UninterpretedTag", true, "setJspAttributes", "(JspAttribute[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "UseBean", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "getBeanName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$UseBean", true, "setBeanName", "(JspAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$VariableDirective", true, "VariableDirective", "(String,Attributes,Attributes,Attributes,Mark,Node)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "doVisit", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(AttributeDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(AttributeGenerator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(Comment)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(CustomTag)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(CustomTag)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(Declaration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(DoBodyAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(ELExpression)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(ELExpression)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(Expression)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(FallBackAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(ForwardAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(GetProperty)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(IncludeAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(IncludeDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(InvokeAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspBody)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspBody)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspElement)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspOutput)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspRoot)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(JspText)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(NamedAttribute)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(PageDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(ParamAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(ParamsAction)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(PlugIn)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(Root)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(Scriptlet)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(SetProperty)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(TagDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(TaglibDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(TemplateText)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(TemplateText)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(UninterpretedTag)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(UseBean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visit", "(VariableDirective)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", true, "visitBody", "(Node)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", true, "accept", "(Visitor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", true, "accept", "(Visitor)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", true, "getLocalName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", true, "getStart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", true, "ServletWriter", "(PrintWriter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "addSmap", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "addSmap", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "addStratum", "(SmapStratum,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "getString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "setOutputFileName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "SmapStratum", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "addFile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "addFile", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "addFile", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "getStratumName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "getString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapUtil", true, "generateSmap", "(JspCompilationContext,Nodes)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TagPluginManager", true, "TagPluginManager", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TagPluginManager", true, "apply", "(Nodes,ErrorDispatcher,PageInfo)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", true, "TldLocationsCache", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", true, "TldLocationsCache", "(ServletContext,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", true, "getLocation", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELContextImpl", false, "ELContextImpl", "(ELResolver)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELContextImpl", false, "setFunctionMapper", "(FunctionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELContextImpl", false, "setVariableMapper", "(VariableMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELContextWrapper", false, "ELContextWrapper", "(ELContext,FunctionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELContextWrapper", false, "ELContextWrapper", "(ELContext,FunctionMapper)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ELResolverImpl", false, "ELResolverImpl", "(VariableResolver)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ExpressionEvaluatorImpl", false, "ExpressionEvaluatorImpl", "(ExpressionFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "ExpressionImpl", false, "ExpressionImpl", "(ValueExpression)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "FunctionMapperImpl", false, "FunctionMapperImpl", "(FunctionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspMethodExpression", false, "JspMethodExpression", "(String,MethodExpression)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspMethodExpression", false, "JspMethodExpression", "(String,MethodExpression)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspValueExpression", false, "JspValueExpression", "(String,ValueExpression)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspValueExpression", false, "JspValueExpression", "(String,ValueExpression)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.el", "VariableResolverImpl", false, "VariableResolverImpl", "(ELContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "InstanceHelper", true, "getServletInstanceManager", "(ServletConfig)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspApplicationContextImpl", true, "createELContext", "(JspContext)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspApplicationContextImpl", true, "getInstance", "(ServletContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", true, "JspContextWrapper", "(JspContext,ArrayList,ArrayList,ArrayList,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", true, "JspContextWrapper", "(JspContext,ArrayList,ArrayList,ArrayList,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", true, "JspContextWrapper", "(JspContext,ArrayList,ArrayList,ArrayList,Map)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", true, "JspContextWrapper", "(JspContext,ArrayList,ArrayList,ArrayList,Map)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", true, "JspContextWrapper", "(JspContext,ArrayList,ArrayList,ArrayList,Map)", "", "Argument[4].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspFragmentHelper", true, "JspFragmentHelper", "(int,JspContext,JspTag)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspFragmentHelper", true, "JspFragmentHelper", "(int,JspContext,JspTag)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspFragmentHelper", true, "getParentTag", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "convert", "(String,String,Class,Class)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "decode", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "getContextRelativePath", "(ServletRequest,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "getContextRelativePath", "(ServletRequest,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "getThrowable", "(ServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", true, "include", "(ServletRequest,ServletResponse,String,JspWriter,boolean)", "", "Argument[2]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspWriterImpl", true, "JspWriterImpl", "(ServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspWriterImpl", true, "JspWriterImpl", "(ServletResponse,int,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "PageContextImpl", true, "getServlet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "ProtectedFunctionMapper", false, "mapFunction", "(String,Class,String,Class[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "ServletResponseWrapperInclude", true, "ServletResponseWrapperInclude", "(ServletResponse,JspWriter)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", true, "get", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", true, "getTagHandlerPool", "(ServletConfig)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", true, "reuse", "(Tag)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JasperLoader", true, "JasperLoader", "(URL[],ClassLoader,PermissionCollection,CodeSource)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JasperLoader", true, "JasperLoader", "(URL[],ClassLoader,PermissionCollection,CodeSource)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JasperLoader", true, "JasperLoader", "(URL[],ClassLoader,PermissionCollection,CodeSource)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspCServletContext", true, "JspCServletContext", "(PrintWriter,ClassLoaderInterface)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspCServletContext", true, "JspCServletContext", "(PrintWriter,ClassLoaderInterface)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletConfig,Options,String,boolean,JspRuntimeContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletConfig,Options,String,boolean,JspRuntimeContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletConfig,Options,String,boolean,JspRuntimeContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletConfig,Options,String,boolean,JspRuntimeContext)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "JspServletWrapper", "(ServletContext,Options,String,TagInfo,JspRuntimeContext,URL)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "getJspEngineContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "getServlet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "getServletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", true, "setCompilationException", "(JasperException)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util$ImportResponseWrapper", true, "getCharEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util$ImportResponseWrapper", true, "getString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util$ImportResponseWrapper", true, "setCharEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", true, "escapeXml", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", true, "getContentTypeAttribute", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", true, "resolveUrl", "(String,String,PageContext)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", true, "resolveUrl", "(String,String,PageContext)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", true, "stripSession", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Collection,boolean)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Iterator)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Iterator,boolean)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.util", "Enumerator", false, "Enumerator", "(Map,boolean)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "ASCIIReader", true, "ASCIIReader", "(InputStream,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", true, "addSymbol", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", true, "addSymbol", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", true, "addSymbol", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", true, "addSymbol", "(char[],int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "TreeNode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "TreeNode", "(String,TreeNode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "TreeNode", "(String,TreeNode)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "TreeNode", "(String,TreeNode)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "addAttribute", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "addAttribute", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "addChild", "(TreeNode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "findAttribute", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "findAttributes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "findChild", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "findChildren", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "findChildren", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "getBody", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "setBody", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "UCSReader", true, "UCSReader", "(InputStream,int,short)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "UCSReader", true, "UCSReader", "(InputStream,short)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "UTF8Reader", true, "UTF8Reader", "(InputStream,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanData", "(String,XMLStringBuffer)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanLiteral", "(int,XMLString)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanPseudoAttribute", "(boolean,XMLString)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanPseudoAttribute", "(boolean,XMLString)", "", "Argument[this]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", true, "scanPseudoAttribute", "(boolean,XMLString)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", true, "XMLString", "(XMLString)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", true, "XMLString", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", true, "setValues", "(XMLString)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", true, "setValues", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "XMLStringBuffer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "XMLStringBuffer", "(XMLString)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "XMLStringBuffer", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "append", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "append", "(XMLString)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", true, "append", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "EmbeddedServletOptions", "(ServletConfig,ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "EmbeddedServletOptions", "(ServletConfig,ServletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "getProperty", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "setProperty", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "setProperty", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", false, "setTldLocationsCache", "(TldLocationsCache)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "generateWebMapping", "(String,JspCompilationContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "generateWebMapping", "(String,JspCompilationContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "getExtensions", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "getSourceCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "scanFiles", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setArgs", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setClassLoaderInterface", "(ClassLoaderInterface)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setClassPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setCompiler", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setCompilerSourceVM", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setCompilerTargetVM", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setIeClassId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setJavaEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setJspFiles", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", true, "setPackage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,TagInfo,Options,ServletContext,JspServletWrapper,JspRuntimeContext,URL)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[5]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "JspCompilationContext", "(String,boolean,Options,ServletContext,JspServletWrapper,JspRuntimeContext,ClassLoaderInterface)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "createCompiler", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getClassFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getClassLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getClassPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getCompiler", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getJavaPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getJspFile", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getJspLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getOptions", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getOutputDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getRealPath", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getRealPath", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getRuntimeContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getServletClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getServletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getServletJavaFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getServletPackageName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getSourceCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getTagFileJarUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getTagFileJarUrl", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getTagInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "getWriter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "resolveRelativeUri", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "resolveRelativeUri", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setClassLoader", "(ClassLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setClassPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setServletClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setServletPackageName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setSourceCode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setTagFileJarUrl", "(String,URL)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setTagFileJarUrl", "(String,URL)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setTagInfo", "(TagInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", true, "setWriter", "(ServletWriter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getCache", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getClassPath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getCompiler", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getCompilerClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getCompilerSourceVM", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getCompilerTargetVM", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getIeClassId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getJavaEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getJspConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getScratchDir", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getTagPluginManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.jasper", "Options", true, "getTldLocationsCache", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", true, "getJsonDateFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", true, "getJsonWriter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", true, "setJsonDateFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", true, "setJsonWriter", "(BeanConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "RPCError", "(String,RPCErrorCode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "RPCError", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "RPCError", "(Throwable,RPCErrorCode,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "RPCError", "(Throwable,int,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "getStack", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "setMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", true, "setStack", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "getDebug", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "getError", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "getId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "getResult", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "setDebug", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "setError", "(RPCError)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCResponse", true, "setResult", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "addSMDMethod", "(SMDMethod)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "getMethods", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "getObjectName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "getServiceType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "getServiceUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "getVersion", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "setObjectName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "setServiceType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "setServiceUrl", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMD", true, "setVersion", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDGenerator", true, "SMDGenerator", "(Object,List,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDGenerator", true, "SMDGenerator", "(Object,List,boolean)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethod", true, "SMDMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethod", true, "addSMDMethodParameter", "(SMDMethodParameter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethod", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethod", true, "getParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethod", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethodParameter", true, "SMDMethodParameter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethodParameter", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDMethodParameter", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner$Filter", true, "Filter", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "clean", "(String,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "getAllowedCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "getBlockedCollection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "getIncludesExcludesMap", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "setAllowed", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "setAllowedCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "setBlocked", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "setBlockedCollection", "(Collection)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", true, "setIncludesExcludesMap", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONException", true, "JSONException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONException", true, "JSONException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONException", true, "JSONException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "getCallbackParameter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "invoke", "(Object,Map)", "", "Argument[1].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setCallbackParameter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setExcludeProperties", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setExcludeWildcards", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setIncludeProperties", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setIncludeWildcards", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setJSONCleaner", "(JSONCleaner)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setJSONPopulator", "(JSONPopulator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setJsonContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setJsonRpcContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", true, "setRoot", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONPopulator", true, "JSONPopulator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONPopulator", true, "convert", "(Class,Type,Object,Method)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONPopulator", true, "getDateFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONPopulator", true, "setDateFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONReader", true, "read", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getCallbackParameter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getDefaultDateFormat", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getExcludePropertiesList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getIncludePropertiesList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getRoot", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getWrapPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "getWrapSuffix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setCallbackParameter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setDefaultDateFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setExcludeProperties", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setExcludeWildcards", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setIncludeProperties", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setIncludeWildcards", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setJsonUtil", "(JSONUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setRoot", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setWrapPrefix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", true, "setWrapSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "asSet", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "processIncludePatterns", "(Set,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String,boolean)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,Collection,Collection,boolean,boolean,boolean,String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Object,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean,boolean)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,Collection,Collection,boolean,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "serialize", "(Writer,Object,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", true, "setWriter", "(JSONWriter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", true, "setNoEncodingSetParam", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", true, "setValidateJsonParam", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", true, "setValidateOnlyParam", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object,Collection,Collection,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object,Collection,Collection,boolean)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object,Collection,Collection,boolean)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", true, "write", "(Object,Collection,Collection,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String)", "", "Argument[10]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[10]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[11]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[12]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "SerializationParams", "(HttpServletResponse,String,boolean,String,boolean,boolean,boolean,int,int,boolean,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getResponse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getSerializedJSON", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getWrapPrefix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", true, "getWrapSuffix", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", true, "normalize", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionRequest", true, "MockActionRequest", "(PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionRequest", true, "MockActionRequest", "(PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionRequest", true, "MockActionRequest", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionRequest", true, "MockActionRequest", "(PortletMode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionResponse", true, "MockActionResponse", "(PortalContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionResponse", true, "getRedirectedUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockBaseURL", true, "getParameter", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockBaseURL", true, "getParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockBaseURL", true, "getParameterValues", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockBaseURL", true, "getProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "MockClientDataRequest", "(PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "MockClientDataRequest", "(PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "MockClientDataRequest", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "setContent", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockClientDataRequest", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEvent", true, "MockEvent", "(QName)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEvent", true, "MockEvent", "(QName,Serializable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEvent", true, "MockEvent", "(QName,Serializable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEvent", true, "MockEvent", "(String,Serializable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event,PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event,PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event,PortalContext,PortletContext)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "MockEventRequest", "(Event,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEventRequest", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "MockMimeResponse", "(PortalContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "MockMimeResponse", "(PortalContext,PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "MockMimeResponse", "(PortalContext,PortletRequest)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "getContentAsByteArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "getContentAsString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "getForwardedUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "getIncludedUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "setCharacterEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "setForwardedUrl", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "setIncludedUrl", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", true, "setLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMultipartActionRequest", true, "addFile", "(MultipartFile)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortalContext", true, "MockPortalContext", "(List,List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortalContext", true, "MockPortalContext", "(List,List)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortalContext", true, "setProperty", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortalContext", true, "setProperty", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "MockPortletConfig", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "MockPortletConfig", "(PortletContext,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "MockPortletConfig", "(PortletContext,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "MockPortletConfig", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addContainerRuntimeOption", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addContainerRuntimeOption", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addContainerRuntimeOption", "(String,String[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addInitParameter", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addInitParameter", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addProcessingEventQName", "(QName)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addPublicRenderParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addPublishingEventQName", "(QName)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "addSupportedLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "setDefaultNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "setResourceBundle", "(Locale,ResourceBundle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletConfig", true, "setResourceBundle", "(Locale,ResourceBundle)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "MockPortletContext", "(ResourceLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "MockPortletContext", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "MockPortletContext", "(String,ResourceLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "MockPortletContext", "(String,ResourceLoader)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "addContainerRuntimeOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "addInitParameter", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "addInitParameter", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletContext", true, "setPortletContextName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletPreferences", true, "setPreferencesValidator", "(PreferencesValidator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletPreferences", true, "setReadOnly", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "MockPortletRequest", "(PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "MockPortletRequest", "(PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "MockPortletRequest", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addParameter", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addParameter", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addParameter", "(String,String[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addPreferredLocale", "(Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addPreferredResponseContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addProperty", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addResponseContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "addUserRole", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "registerPublicParameter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setAuthType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setContextPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setCookies", "(Cookie[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setParameter", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setParameter", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setParameter", "(String,String[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setParameters", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setPortletMode", "(PortletMode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setPreferences", "(PortletPreferences)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setProperty", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setRemoteUser", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setServerName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setSession", "(PortletSession)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setUserPrincipal", "(Principal)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setWindowID", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", true, "setWindowState", "(WindowState)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequestDispatcher", true, "MockPortletRequestDispatcher", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "MockPortletResponse", "(PortalContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getCookie", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getCookies", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getPortalContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getProperties", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getProperty", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getPropertyNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getXmlProperties", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getXmlProperty", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "getXmlPropertyNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletResponse", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletSession", true, "MockPortletSession", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletURL", true, "MockPortletURL", "(PortalContext,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletURL", true, "MockPortletURL", "(PortalContext,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortletMode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortletMode,WindowState)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderRequest", true, "MockRenderRequest", "(PortletMode,WindowState)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderResponse", true, "MockRenderResponse", "(PortalContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderResponse", true, "MockRenderResponse", "(PortalContext,RenderRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderResponse", true, "MockRenderResponse", "(PortalContext,RenderRequest)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderResponse", true, "getNextPossiblePortletModes", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockRenderResponse", true, "getTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "MockResourceRequest", "(MockResourceURL)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "MockResourceRequest", "(PortalContext,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "MockResourceRequest", "(PortalContext,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "MockResourceRequest", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "MockResourceRequest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "addPrivateRenderParameter", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "addPrivateRenderParameter", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "addPrivateRenderParameter", "(String,String[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "setCacheability", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceRequest", true, "setResourceID", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceURL", true, "getResourceID", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "MockStateAwareResponse", "(PortalContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getEvent", "(QName)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getEvent", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getEventNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getRenderParameter", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getRenderParameterNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockStateAwareResponse", true, "getRenderParameterValues", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "ServletWrappingPortletContext", true, "ServletWrappingPortletContext", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "ServletWrappingPortletContext", true, "getServletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi.action", "BundleContextAware", true, "withBundleContext", "(BundleContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", true, "getId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", true, "setBundleAccessor", "(BundleAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", true, "setConfiguration", "(Configuration)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "ShellAction", true, "getCommand", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "ShellAction", true, "getOutput", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "ShellAction", true, "setCommand", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", true, "getBundleContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", true, "init", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", true, "init", "(ServletContext)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.interceptor", "BundleContextAware", true, "setBundleContext", "(BundleContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.interceptor", "OsgiInterceptor", true, "setServletContext", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi.loaders", "StaticContentBundleResourceLoader", true, "setBundleAccessor", "(BundleAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", true, "addPackageFromBundle", "(Bundle,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", true, "addPackageFromBundle", "(Bundle,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", true, "getPackagesByBundle", "(Bundle)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", true, "setBundleContext", "(BundleContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", true, "setOsgiHost", "(OsgiHost)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "DelegatingObjectFactory", true, "setBundleResourceLoader", "(BundleAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "DelegatingObjectFactory", true, "setOsgiConfigurationProvider", "(PackageProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", true, "setBundleAccessor", "(BundleAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", true, "setFileManagerFactory", "(FileManagerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", true, "setServletContext", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiUtil", true, "generateJavaVersionForSystemPackages", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiUtil", true, "translateBundleURLToJarURL", "(URL,Bundle)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "PackageLoader", true, "loadPackages", "(Container,Bundle,BundleContext,ObjectFactory,FileManagerFactory,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.osgi", "SpringOsgiObjectFactory", true, "setBundleAccessor", "(BundleAccessor)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "DefaultOValValidationManager", true, "setFileManagerFactory", "(FileManagerFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationInterceptor", true, "setTextProviderFactory", "(TextProviderFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationInterceptor", true, "setValidationManager", "(OValValidationManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationManager", true, "getConfigurers", "(Class,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationManager", true, "getConfigurers", "(Class,String,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationManager", true, "getConfigurers", "(Class,String,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusObjectFactory", true, "setReflectionProvider", "(ReflectionProvider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusObjectFactory", true, "setServletConfig", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "DirectRenderFromEventAction", true, "getLocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[1].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[2].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[5]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[6]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[7]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[8]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[9]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "createContextMap", "(Map,Map,Map,Map,PortletRequest,PortletResponse,HttpServletRequest,HttpServletResponse,ServletContext,PortletConfig,PortletPhase)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.interceptor", "PortletPrincipalProxy", true, "PortletPrincipalProxy", "(PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.interceptor", "ServletPortletPreferences", true, "ServletPortletPreferences", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "PortletActionRedirectResult", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "addParameter", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "addParameter", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletActionRedirectResult", true, "setQueryStringBuilder", "(QueryStringBuilder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", true, "PortletResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", true, "setDispatcherServletName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", true, "setTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResultHelper", true, "include", "(PortletRequestDispatcher,String,PortletRequest,PortletResponse)", "", "Argument[0]", "Argument[3]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResultHelper", true, "include", "(PortletRequestDispatcher,String,PortletRequest,PortletResponse)", "", "Argument[1]", "Argument[3]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResultHelper", true, "setPortletMode", "(PortletResponse,PortletMode)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResultHelper", true, "setRenderParameter", "(PortletResponse,String,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletVelocityResult", true, "PortletVelocityResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletVelocityResult", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletVelocityResult", true, "setVelocityManager", "(VelocityManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletHttpSession", true, "PortletHttpSession", "(PortletSession)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletHttpSession", true, "getPortletSession", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletConfig", true, "PortletServletConfig", "(PortletConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletConfig", true, "getPortletConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletContext", true, "PortletServletContext", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletContext", true, "getPortletContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletInputStream", true, "PortletServletInputStream", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletInputStream", true, "getInputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletOutputStream", true, "PortletServletOutputStream", "(OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletOutputStream", true, "getOutputStream", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "PortletServletRequest", "(PortletRequest,PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "PortletServletRequest", "(PortletRequest,PortletContext)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "PortletServletRequest", "(PortletRequest,PortletContext,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "PortletServletRequest", "(PortletRequest,PortletContext,Map)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "PortletServletRequest", "(PortletRequest,PortletContext,Map)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequest", true, "getPortletRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletRequestDispatcher", true, "PortletServletRequestDispatcher", "(PortletRequestDispatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletResponse", true, "PortletServletResponse", "(PortletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletResponse", true, "getPortletResponse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.servlet", "PortletServletResponseJSR286", true, "PortletServletResponseJSR286", "(PortletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildResourceUrl", "(String,Map)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String)", "", "Argument[0]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String)", "", "Argument[1]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String)", "", "Argument[2]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String)", "", "Argument[5]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String,String,boolean,boolean)", "", "Argument[0]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String,String,boolean,boolean)", "", "Argument[1]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String,String,boolean,boolean)", "", "Argument[2]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "buildUrl", "(String,String,String,Map,String,String,String,String,boolean,boolean)", "", "Argument[6]", "Argument[3].Element", "taint", "df-generated"] + - ["org.apache.struts2.portlet.util", "PortletUrlHelper", true, "ensureParamsAreStringArrays", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet", "PortletApplicationMap", true, "PortletApplicationMap", "(PortletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet", "PortletApplicationMap", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet", "PortletApplicationMap", true, "remove", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.portlet", "PortletRequestMap", true, "PortletRequestMap", "(PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.portlet", "PortletSessionMap", true, "PortletSessionMap", "(PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperDeleteMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperEditMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperGetMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperIndexMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperNewMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperOptionsMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperPostContinueMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperPostMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperPutContinueMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getMapperPutMethodName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getRestDefaultErrorResultName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getRestDefaultExtension", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getRestNamespace", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "getRestValidationFailureStatusCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperDeleteMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperEditMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperGetMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperIndexMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperNewMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperOptionsMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperPostContinueMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperPostMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperPutContinueMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setMapperPutMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setRestDefaultErrorResultName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setRestDefaultExtension", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setRestNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", true, "setRestValidationFailureStatusCode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", true, "fromObject", "(ActionInvocation,Object,String,Writer)", "", "Argument[1]", "Argument[3]", "taint", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", true, "fromObject", "(ActionInvocation,Object,String,Writer)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest.handler", "JacksonJsonHandler", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "findExtension", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "findExtension", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "getHandlerForRequest", "(HttpServletRequest)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "getHandlerForResponse", "(HttpServletRequest,HttpServletResponse)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "handleResult", "(ActionConfig,Object,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeHandlerManager", true, "handleResult", "(ActionInvocation,Object,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "ContentTypeInterceptor", true, "setContentTypeHandlerSelector", "(ContentTypeHandlerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultContentTypeHandlerManager", true, "getExtensionIfPresent", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultContentTypeHandlerManager", true, "setDefaultExtension", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "DefaultHttpHeaders", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "disableCaching", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "lastModified", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "lastModified", "(Date)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "renderResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "renderResult", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "setLocation", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "setLocation", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "setLocationId", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "setLocationId", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "withETag", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "withETag", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "withNoETag", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "DefaultHttpHeaders", true, "withStatus", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.rest", "HttpHeaders", true, "apply", "(HttpServletRequest,HttpServletResponse,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "HttpHeaders", true, "getResultCode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionInvocation", true, "setDefaultErrorResultName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionInvocation", true, "setMimeTypeHandlerSelector", "(ContentTypeHandlerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "getIdParameterName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setDeleteMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setEditMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setGetMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setIdParameterName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setIndexMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setNewMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setOptionsMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setPostContinueMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setPostMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setPutContinueMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionMapper", true, "setPutMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestActionProxyFactory", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setContentTypeHandlerManager", "(ContentTypeHandlerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setEditMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setInputResultName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setNewMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setPostMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", true, "setPutMethodName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "getBody", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "getCookies", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "getDateHeaders", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "getIntHeaders", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "getStringHeaders", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withContentType", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withContentTypeJson", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withContentTypeTextHtml", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withContentTypeTextPlain", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withCookie", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withHeader", "(String,Integer)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withHeader", "(String,Long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "withHeader", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "write", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "write", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "writeLine", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.plain", "ResponseBuilder", true, "writeLine", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result.xslt", "AbstractAdapterNode", true, "getChildBeforeOrAfter", "(Node,boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AbstractAdapterNode", true, "getElementsByTagName", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AbstractAdapterNode", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptDocument", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptDocument", "(String,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptDocument", "(String,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNode", "(AdapterNode,String,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNode", "(AdapterNode,String,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNode", "(AdapterNode,String,Object)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNode", "(AdapterNode,String,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNullValue", "(AdapterNode,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNullValue", "(AdapterNode,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "adaptNullValue", "(AdapterNode,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNamedNodeMap", "(AdapterNode,NamedNodeMap)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNamedNodeMap", "(AdapterNode,NamedNodeMap)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNamedNodeMap", "(AdapterNode,NamedNodeMap)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNode", "(AdapterNode,Node)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNode", "(AdapterNode,Node)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", true, "proxyNode", "(AdapterNode,Node)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getAdapterFactory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getChildAfter", "(Node)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getChildBefore", "(Node)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getParent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getPropertyName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "getPropertyValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "setAdapterFactory", "(AdapterFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "setParent", "(AdapterNode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "setPropertyName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", true, "setPropertyValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ArrayAdapter", true, "ArrayAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ArrayAdapter", true, "ArrayAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ArrayAdapter", true, "ArrayAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ArrayAdapter", true, "ArrayAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "BeanAdapter", true, "BeanAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "BeanAdapter", true, "BeanAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "BeanAdapter", true, "BeanAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "BeanAdapter", true, "BeanAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "CollectionAdapter", true, "CollectionAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "CollectionAdapter", true, "CollectionAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "CollectionAdapter", true, "CollectionAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "CollectionAdapter", true, "CollectionAdapter", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "MapAdapter", true, "MapAdapter", "(AdapterFactory,AdapterNode,String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "MapAdapter", true, "MapAdapter", "(AdapterFactory,AdapterNode,String,Map)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "MapAdapter", true, "MapAdapter", "(AdapterFactory,AdapterNode,String,Map)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "MapAdapter", true, "MapAdapter", "(AdapterFactory,AdapterNode,String,Map)", "", "Argument[3].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "MapAdapter", true, "map", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyAttrAdapter", true, "ProxyAttrAdapter", "(AdapterFactory,AdapterNode,Attr)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyAttrAdapter", true, "ProxyAttrAdapter", "(AdapterFactory,AdapterNode,Attr)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyAttrAdapter", true, "ProxyAttrAdapter", "(AdapterFactory,AdapterNode,Attr)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyAttrAdapter", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyElementAdapter", true, "ProxyElementAdapter", "(AdapterFactory,AdapterNode,Element)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyElementAdapter", true, "ProxyElementAdapter", "(AdapterFactory,AdapterNode,Element)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyElementAdapter", true, "ProxyElementAdapter", "(AdapterFactory,AdapterNode,Element)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyElementAdapter", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyNamedNodeMap", true, "ProxyNamedNodeMap", "(AdapterFactory,AdapterNode,NamedNodeMap)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyNamedNodeMap", true, "ProxyNamedNodeMap", "(AdapterFactory,AdapterNode,NamedNodeMap)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyNamedNodeMap", true, "ProxyNamedNodeMap", "(AdapterFactory,AdapterNode,NamedNodeMap)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyNodeAdapter", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyTextNodeAdapter", true, "ProxyTextNodeAdapter", "(AdapterFactory,AdapterNode,Text)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyTextNodeAdapter", true, "ProxyTextNodeAdapter", "(AdapterFactory,AdapterNode,Text)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyTextNodeAdapter", true, "ProxyTextNodeAdapter", "(AdapterFactory,AdapterNode,Text)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ProxyTextNodeAdapter", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "ServletURIResolver", true, "ServletURIResolver", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleAdapterDocument", true, "SimpleAdapterDocument", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleAdapterDocument", true, "SimpleAdapterDocument", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleAdapterDocument", true, "SimpleAdapterDocument", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleAdapterDocument", true, "SimpleAdapterDocument", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleNodeList", true, "SimpleNodeList", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleNodeList", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleTextNode", true, "SimpleTextNode", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleTextNode", true, "SimpleTextNode", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleTextNode", true, "SimpleTextNode", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "SimpleTextNode", true, "SimpleTextNode", "(AdapterFactory,AdapterNode,String,Object)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", true, "StringAdapter", "(AdapterFactory,AdapterNode,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", true, "StringAdapter", "(AdapterFactory,AdapterNode,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", true, "StringAdapter", "(AdapterFactory,AdapterNode,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", true, "StringAdapter", "(AdapterFactory,AdapterNode,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "XSLTResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "getEncoding", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "getExposedValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "getStylesheetLocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "setExposedValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", true, "setStylesheetLocation", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", true, "addHeader", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", true, "addHeader", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", true, "getHeaders", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", true, "setError", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", true, "setErrorMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PlainTextResult", true, "PlainTextResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PlainTextResult", true, "getCharSet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "PlainTextResult", true, "setCharSet", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "ServletActionRedirectResult", "(String,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "setActionName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletActionRedirectResult", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletDispatcherResult", true, "ServletDispatcherResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletDispatcherResult", true, "setQueryStringParser", "(QueryStringParser)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "ServletRedirectResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "ServletRedirectResult", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "ServletRedirectResult", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "addParameter", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "addParameter", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "setActionMapper", "(ActionMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", true, "setQueryStringBuilder", "(QueryStringBuilder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "StreamResult", "(InputStream)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "getContentCharSet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "getContentDisposition", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "getContentLength", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "getInputName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setContentCharSet", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setContentDisposition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setContentLength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setInputName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", true, "setNotExcludedAcceptedPatterns", "(NotExcludedAcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "StrutsResultSupport", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "StrutsResultSupport", "(String,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "doExecute", "(String,ActionInvocation)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "doExecute", "(String,ActionInvocation)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "getLastFinalLocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "getLocation", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", true, "setLocation", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "AbstractCRUDAction", true, "getAvailableItems", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "AbstractCRUDAction", true, "getToDelete", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "AbstractCRUDAction", true, "setToDelete", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", true, "getCurrentEmployee", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", true, "getSelectedSkills", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", true, "setCurrentEmployee", "(Employee)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", true, "setSelectedSkills", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "JSPEvalAction", true, "setJsp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "SkillAction", true, "getCurrentSkill", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "SkillAction", true, "getSkillName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "SkillAction", true, "setCurrentSkill", "(Skill)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.action", "SkillAction", true, "setSkillName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain1", true, "getActionChain1Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain1", true, "setActionChain1Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain2", true, "getActionChain1Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain2", true, "getActionChain2Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain2", true, "setActionChain1Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain2", true, "setActionChain2Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "getActionChain1Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "getActionChain2Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "getActionChain3Property1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "setActionChain1Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "setActionChain2Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.actionchaining", "ActionChain3", true, "setActionChain3Property1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "Category", "(long,String,Category[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "Category", "(long,String,Category[])", "", "Argument[2].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "getChildren", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "setChildren", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "GetCategory", true, "getCategory", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AjaxTestAction", true, "getData", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AjaxTestAction", true, "setData", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AutocompleterExampleAction", true, "getOptions", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AutocompleterExampleAction", true, "getSelect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AutocompleterExampleAction", true, "setSelect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", true, "getGender", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", true, "setGender", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example5Action", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example5Action", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", true, "findAll", "(Class)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", true, "get", "(Class,Serializable)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", true, "update", "(IdEntity)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.async", "ChatRoomAction", true, "getNewMessages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.async", "ChatRoomAction", true, "setMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatException", true, "ChatException", "(String,ErrorType)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatLoginAction", true, "ChatLoginAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatLoginAction", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatLoginAction", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatLogoutAction", true, "ChatLogoutAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatMessage", true, "ChatMessage", "(String,User)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatMessage", true, "ChatMessage", "(String,User)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatMessage", true, "getCreationDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatMessage", true, "getCreator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatMessage", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "addRoom", "(Room)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "getAvailableRooms", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "getAvailableUsers", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "getMessagesInRoom", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "getUsersAvailableInRoom", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", true, "login", "(User)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", true, "CrudRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", true, "setDescription", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "DateConverter", true, "setXWorkConverter", "(XWorkConverter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "EnterRoomAction", true, "EnterRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "EnterRoomAction", true, "getRoomName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "EnterRoomAction", true, "setRoomName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ExitRoomAction", true, "ExitRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ExitRoomAction", true, "getRoomName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ExitRoomAction", true, "setRoomName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "MessagesAvailableInRoomAction", true, "MessagesAvailableInRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "MessagesAvailableInRoomAction", true, "getMessagesAvailableInRoom", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "MessagesAvailableInRoomAction", true, "getRoomName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "MessagesAvailableInRoomAction", true, "setRoomName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "Room", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "Room", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "addMessage", "(ChatMessage)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "findMember", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "getChatMessages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "getCreationDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "getMembers", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", true, "memberEnter", "(User)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "RoomsAvailableAction", true, "RoomsAvailableAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "RoomsAvailableAction", true, "getAvailableRooms", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "SendMessageToRoomAction", true, "SendMessageToRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "SendMessageToRoomAction", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "SendMessageToRoomAction", true, "getRoomName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "SendMessageToRoomAction", true, "setMessage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "SendMessageToRoomAction", true, "setRoomName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "User", true, "User", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "User", true, "getCreationDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "User", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableAction", true, "UsersAvailableAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableAction", true, "getAvailableUsers", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableInRoomAction", true, "UsersAvailableInRoomAction", "(ChatService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableInRoomAction", true, "getRoomName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableInRoomAction", true, "getUsersAvailableInRoom", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.chat", "UsersAvailableInRoomAction", true, "setRoomName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Address", true, "getAddress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Address", true, "getId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Address", true, "setAddress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Address", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "AddressAction", true, "getAddresses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "AddressAction", true, "setAddresses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "OperationsEnumAction", true, "getSelectedOperations", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "OperationsEnumAction", true, "setSelectedOperations", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Person", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Person", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "PersonAction", true, "getPersons", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "PersonAction", true, "setPersons", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "AbstractDao", true, "getStorage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "AbstractDao", true, "setStorage", "(Storage)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", true, "findAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", true, "get", "(Serializable)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", true, "update", "(IdEntity)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "EmployeeDao", true, "getEmployee", "(Long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "EmployeeDao", true, "setSkillDao", "(SkillDao)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "EmployeeDao", true, "setSkills", "(Employee,List)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "EmployeeDao", true, "setSkills", "(Long,List)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.dao", "SkillDao", true, "getSkill", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "CreateException", true, "CreateException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "CreateException", true, "CreateException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "CreateException", true, "CreateException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DeleteException", true, "DeleteException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DeleteException", true, "DeleteException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DeleteException", true, "DeleteException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DuplicateKeyException", true, "DuplicateKeyException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DuplicateKeyException", true, "DuplicateKeyException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DuplicateKeyException", true, "DuplicateKeyException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "StorageException", true, "StorageException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "StorageException", true, "StorageException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "StorageException", true, "StorageException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "UpdateException", true, "UpdateException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "UpdateException", true, "UpdateException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.exception", "UpdateException", true, "UpdateException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.filedownload", "FileDownloadAction", true, "setInputPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "getCaption", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "getUpload", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "getUploadContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "getUploadFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "setCaption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "setUpload", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "setUploadContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", true, "setUploadFileName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "getUpload", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "getUploadContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "getUploadFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "setUpload", "(File[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "setUploadContentType", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", true, "setUploadFileName", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "getUpload", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "getUploadContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "getUploadFileName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "setUpload", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "setUploadContentType", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", true, "setUploadFileName", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "CustomFreemarkerManager", true, "CustomFreemarkerManager", "(CustomFreemarkerManagerUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "getGender", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "getMonths", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "setGender", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "setMonths", "(String[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "StandardTagsAction", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "GetUpdatedHangmanAction", true, "getHangman", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "GetUpdatedHangmanAction", true, "setHangman", "(Hangman)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "GuessCharacterAction", true, "getHangman", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", true, "Hangman", "(Vocab)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", true, "getCharactersAvailable", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", true, "getVocab", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "HangmanException", true, "HangmanException", "(Type,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "HangmanService", true, "HangmanService", "(VocabSource)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "HangmanService", true, "startNewGame", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "PropertiesVocabSource", true, "PropertiesVocabSource", "(Properties)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "PropertiesVocabSource", true, "setVocabProperties", "(Properties)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "StartHangmanAction", true, "StartHangmanAction", "(HangmanService)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "StartHangmanAction", true, "getHangman", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", true, "Vocab", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", true, "Vocab", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", true, "getHint", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", true, "getVocab", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", true, "inCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "VocabSource", true, "getRandomVocab", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[10]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[11]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[6]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[7]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[8].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "Employee", "(Long,String,String,Date,Float,boolean,String,Skill,List,String,String,String)", "", "Argument[9]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getBirthDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getComment", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getFirstName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getLastName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getLevel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getMainSkill", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getOtherSkills", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getPassword", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "getPosition", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setBirthDate", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setComment", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setFirstName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setLastName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setLevel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setMainSkill", "(Skill)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setOtherSkills", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setPassword", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", true, "setPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "IdEntity", true, "setId", "(Serializable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", true, "Skill", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", true, "Skill", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", true, "setDescription", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", true, "setDescription", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "EditPersonAction", true, "getPersons", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "EditPersonAction", true, "setPersons", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "ListPeopleAction", true, "getPeople", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "NewPersonAction", true, "getPerson", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "NewPersonAction", true, "setPerson", "(Person)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "Person", "(Long,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "Person", "(Long,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "getLastName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "setLastName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getClassLines", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getClassName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getConfigLines", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getPage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "getPageLines", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "setClassName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "setConfig", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", true, "setPage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.actionPrefix", "SubmitAction", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.actionPrefix", "SubmitAction", true, "setText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "AppendIteratorTagDemo", true, "getIteratorValue1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "AppendIteratorTagDemo", true, "getIteratorValue2", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "AppendIteratorTagDemo", true, "setIteratorValue1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "AppendIteratorTagDemo", true, "setIteratorValue2", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", true, "getSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", true, "setSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "MergeIteratorTagDemo", true, "getIteratorValue1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "MergeIteratorTagDemo", true, "getIteratorValue2", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "MergeIteratorTagDemo", true, "setIteratorValue1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "MergeIteratorTagDemo", true, "setIteratorValue2", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", true, "getIteratorValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", true, "setIteratorValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getDateValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getEmailValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getFieldExpressionValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getRegexValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getRequiredStringValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getRequiredValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getStringLengthValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "getUrlValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setDateValidatorField", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setEmailValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setFieldExpressionValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setRegexValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setRequiredStringValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setRequiredValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setStringLengthValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", true, "setUrlValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getDateValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getEmailValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getFieldExpressionValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getRegexValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getRequiredStringValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getRequiredValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getStringLengthValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "getUrlValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setDateValidatorField", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setEmailValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setFieldExpressionValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setRegexValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setRequiredStringValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setRequiredValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setStringLengthValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", true, "setUrlValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getDateValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getEmailValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getFieldExpressionValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getRegexValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getRequiredStringValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getRequiredValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getStringLengthValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "getUrlValidatorField", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setDateValidatorField", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setEmailValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setFieldExpressionValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setRegexValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setRequiredStringValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setRequiredValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setStringLengthValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", true, "setUrlValidatorField", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "getSomeText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "getSomeTextRetype", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "getSomeTextRetypeAgain", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "setSomeText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "setSomeTextRetype", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "NonFieldValidatorsExampleAction", true, "setSomeTextRetypeAgain", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", true, "getAnswer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", true, "setAnswer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", true, "getBirthday", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", true, "setBirthday", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "VisitorValidatorsExampleAction", true, "getUser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.validation", "VisitorValidatorsExampleAction", true, "setUser", "(User)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "ImportantInfo", "(String,Properties)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "ImportantInfo", "(String,Properties)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "getClasspath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "getSystemProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "setClasspath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction$ImportantInfo", true, "setSystemProperties", "(Properties)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", true, "getEnvironment", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", true, "getInfo", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", true, "getServletRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", true, "setEnvironment", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", true, "setInfo", "(ImportantInfo)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", true, "getAfter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", true, "getBefore", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", true, "getFuture", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", true, "getNow", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", true, "getPast", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "DynamicTreeSelectAction", true, "getNodeName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteCars", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteCities", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteCountries", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteMotorcycles", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getFavouriteSports", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getNonFavouriteSports", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getNotFavouriteCars", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getNotFavouriteCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getNotFavouriteCountries", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getNotFavouriteMotorcycles", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getPrioritisedFavouriteCars", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getPrioritisedFavouriteCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "getPrioritisedFavouriteCountries", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteCars", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteCities", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteCountries", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteMotorcycles", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setFavouriteSports", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setNonFavouriteSports", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setNotFavouriteCars", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setNotFavouriteCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setNotFavouriteCountries", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setNotFavouriteMotorcycles", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setPrioritisedFavouriteCars", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setPrioritisedFavouriteCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", true, "setPrioritisedFavouriteCountries", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "getDescription1", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "getDescription2", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "getDescription3", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "getDescription4", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "setDescription1", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "setDescription2", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "setDescription3", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", true, "setDescription4", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "getFavouriteCities", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "getFavouriteNumbers", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "getPrioritisedFavouriteCars", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "getPrioritisedFavouriteCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "getPrioritisedFavouriteCountries", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "setFavouriteCities", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "setFavouriteNumbers", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "setPrioritisedFavouriteCars", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "setPrioritisedFavouriteCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", true, "setPrioritisedFavouriteCountries", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "Language", "(String,String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "Language", "(String,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "Language", "(String,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "getKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$Language", true, "getStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalSpecific", true, "VehicalSpecific", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalSpecific", true, "VehicalSpecific", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalSpecific", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalSpecific", true, "getKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalType", true, "VehicalType", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalType", true, "VehicalType", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalType", true, "getDescription", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample$VehicalType", true, "getKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getBestFriend", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getBio", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getBirthday", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFavouriteColor", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFavouriteLanguage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFavouriteLanguages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFavouriteVehicalSpecific", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFavouriteVehicalType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getFriends", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getLeftSideCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getPicture", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getRegion", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getRightSideCartoonCharacters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getState", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getThoughts", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getVehicalSpecificList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getVehicalTypeList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "getWakeup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setBestFriend", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setBio", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setBirthday", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setFavouriteColor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setFavouriteLanguage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setFavouriteVehicalSpecific", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setFavouriteVehicalType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setFriends", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setLeftSideCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setPicture", "(File)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setPictureContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setPictureFileName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setRegion", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setRightSideCartoonCharacters", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setThoughts", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", true, "setWakeup", "(Date)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.sitemesh", "FreemarkerMapper2DecoratorSelector", true, "FreemarkerMapper2DecoratorSelector", "(DecoratorMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.sitemesh", "OldDecorator2NewStrutsDecorator", true, "OldDecorator2NewStrutsDecorator", "(Decorator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.sitemesh", "OldDecorator2NewStrutsFreemarkerDecorator", true, "OldDecorator2NewStrutsFreemarkerDecorator", "(Decorator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.sitemesh", "OldDecorator2NewStrutsVelocityDecorator", true, "OldDecorator2NewStrutsVelocityDecorator", "(Decorator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.sitemesh", "VelocityMapper2DecoratorSelector", true, "VelocityMapper2DecoratorSelector", "(DecoratorMapper)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", true, "getClassReloadingAcceptClasses", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", true, "getClassReloadingWatchList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", true, "setClassReloadingAcceptClasses", "(Set)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", true, "setClassReloadingWatchList", "(List)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.spring", "ClassReloadingXMLWebApplicationContext", true, "getReloadingClassLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.tiles", "StrutsApplicationResource", true, "StrutsApplicationResource", "(URL)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.tiles", "StrutsApplicationResource", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringBuilder", true, "build", "(Map,StringBuilder,String)", "", "Argument[0].Element", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringBuilder", true, "build", "(Map,StringBuilder,String)", "", "Argument[2]", "Argument[1]", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "addParam", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "addParam", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "addParam", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "getQueryFragment", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "getQueryParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "withQueryFragment", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", true, "withQueryFragment", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser", true, "parse", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser", true, "parse", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "StrutsQueryStringBuilder", true, "StrutsQueryStringBuilder", "(UrlEncoder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "StrutsQueryStringParser", true, "StrutsQueryStringParser", "(UrlDecoder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "StrutsUrlDecoder", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "StrutsUrlEncoder", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.url", "UrlDecoder", true, "decode", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "UrlDecoder", true, "decode", "(String,String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "UrlDecoder", true, "decode", "(String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "UrlEncoder", true, "encode", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.url", "UrlEncoder", true, "encode", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "AppendIteratorFilter", true, "setSource", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "AttributeMap", true, "AttributeMap", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "ComponentUtils", true, "stripExpression", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", true, "toByteArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", true, "writeTo", "(JspWriter,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", true, "writeTo", "(OutputStream)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", true, "writeTo", "(Writer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.util", "IteratorFilterSupport$EnumerationIterator", true, "EnumerationIterator", "(Enumeration)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", true, "getNext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", true, "setConverter", "(Converter)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", true, "setSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", true, "setValues", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "ListEntry", true, "ListEntry", "(Object,Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "ListEntry", true, "ListEntry", "(Object,Object,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "ListEntry", true, "getKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "ListEntry", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "MakeIterator", true, "convert", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "MergeIteratorFilter", true, "setSource", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "RegexPatternMatcherExpression", true, "RegexPatternMatcherExpression", "(Pattern,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "RegexPatternMatcherExpression", true, "RegexPatternMatcherExpression", "(Pattern,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "RegexPatternMatcherExpression", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "RegexPatternMatcherExpression", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "SortIteratorFilter", true, "getList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "SortIteratorFilter", true, "setComparator", "(Comparator)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "SortIteratorFilter", true, "setSource", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsTestCaseHelper", true, "initDispatcher", "(ServletContext,Map)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsTestCaseHelper", true, "initDispatcher", "(ServletContext,Map)", "", "Argument[1].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsTypeConverter", true, "convertFromString", "(Map,String[],Class)", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsTypeConverter", true, "convertToString", "(Map,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "StrutsUtil", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "StrutsUtil", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "StrutsUtil", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "bean", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "buildUrl", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "buildUrl", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "findString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "findValue", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "getContext", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "getText", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "makeSelectList", "(String,String,String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", true, "urlEncode", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "SubsetIteratorFilter", true, "setDecider", "(Decider)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "SubsetIteratorFilter", true, "setSource", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", true, "getContent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", true, "getTabAlign", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", true, "setContent", "(Vector)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", true, "setTabAlign", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "TextProviderHelper", true, "getText", "(String,String,List,ValueStack)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "TextProviderHelper", true, "getText", "(String,String,ValueStack)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", true, "buildTokenSessionAttributeName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "addParameter", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "addParameter", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "getURL", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "setPage", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "setPage", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "setRequest", "(HttpServletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "setResponse", "(HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.util", "URLBean", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionErrorModel", true, "ActionErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionErrorModel", true, "ActionErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionErrorModel", true, "ActionErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionMessageModel", true, "ActionMessageModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionMessageModel", true, "ActionMessageModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionMessageModel", true, "ActionMessageModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionModel", true, "ActionModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionModel", true, "ActionModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ActionModel", true, "ActionModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "AnchorModel", true, "AnchorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "AnchorModel", true, "AnchorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "AnchorModel", true, "AnchorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "BeanModel", true, "BeanModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "BeanModel", true, "BeanModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "BeanModel", true, "BeanModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CallbackWriter", true, "CallbackWriter", "(Component,Writer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CallbackWriter", true, "CallbackWriter", "(Component,Writer)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CallbackWriter", true, "getBean", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxListModel", true, "CheckboxListModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxListModel", true, "CheckboxListModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxListModel", true, "CheckboxListModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxModel", true, "CheckboxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxModel", true, "CheckboxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "CheckboxModel", true, "CheckboxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComboBoxModel", true, "ComboBoxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComboBoxModel", true, "ComboBoxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComboBoxModel", true, "ComboBoxModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComponentModel", true, "ComponentModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComponentModel", true, "ComponentModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ComponentModel", true, "ComponentModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DateModel", true, "DateModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DateModel", true, "DateModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DateModel", true, "DateModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DoubleSelectModel", true, "DoubleSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DoubleSelectModel", true, "DoubleSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "DoubleSelectModel", true, "DoubleSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseIfModel", true, "ElseIfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseIfModel", true, "ElseIfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseIfModel", true, "ElseIfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseModel", true, "ElseModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseModel", true, "ElseModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ElseModel", true, "ElseModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FieldErrorModel", true, "FieldErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FieldErrorModel", true, "FieldErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FieldErrorModel", true, "FieldErrorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FileModel", true, "FileModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FileModel", true, "FileModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FileModel", true, "FileModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FormModel", true, "FormModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FormModel", true, "FormModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "FormModel", true, "FormModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HeadModel", true, "HeadModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HeadModel", true, "HeadModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HeadModel", true, "HeadModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HiddenModel", true, "HiddenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HiddenModel", true, "HiddenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "HiddenModel", true, "HiddenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "I18nModel", true, "I18nModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "I18nModel", true, "I18nModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "I18nModel", true, "I18nModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IfModel", true, "IfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IfModel", true, "IfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IfModel", true, "IfModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IncludeModel", true, "IncludeModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IncludeModel", true, "IncludeModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IncludeModel", true, "IncludeModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "InputTransferSelectModel", true, "InputTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "InputTransferSelectModel", true, "InputTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "InputTransferSelectModel", true, "InputTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IteratorModel", true, "IteratorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IteratorModel", true, "IteratorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "IteratorModel", true, "IteratorModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LabelModel", true, "LabelModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LabelModel", true, "LabelModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LabelModel", true, "LabelModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LinkModel", true, "LinkModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LinkModel", true, "LinkModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "LinkModel", true, "LinkModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptGroupModel", true, "OptGroupModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptGroupModel", true, "OptGroupModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptGroupModel", true, "OptGroupModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptionTransferSelectModel", true, "OptionTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptionTransferSelectModel", true, "OptionTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "OptionTransferSelectModel", true, "OptionTransferSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ParamModel", true, "ParamModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ParamModel", true, "ParamModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ParamModel", true, "ParamModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PasswordModel", true, "PasswordModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PasswordModel", true, "PasswordModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PasswordModel", true, "PasswordModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PropertyModel", true, "PropertyModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PropertyModel", true, "PropertyModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PropertyModel", true, "PropertyModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PushModel", true, "PushModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PushModel", true, "PushModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "PushModel", true, "PushModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "RadioModel", true, "RadioModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "RadioModel", true, "RadioModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "RadioModel", true, "RadioModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ResetModel", true, "ResetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ResetModel", true, "ResetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ResetModel", true, "ResetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ScriptModel", true, "ScriptModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ScriptModel", true, "ScriptModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "ScriptModel", true, "ScriptModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SelectModel", true, "SelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SelectModel", true, "SelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SelectModel", true, "SelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SetModel", true, "SetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SetModel", true, "SetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SetModel", true, "SetModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "StrutsModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "StrutsModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "StrutsModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getA", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getAction", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getActionerror", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getActionmessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getBean", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getCheckbox", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getCheckboxlist", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getCombobox", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getComponent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getDate", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getDoubleselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getElse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getElseif", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getFielderror", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getFile", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getForm", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getHead", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getHidden", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getHref", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getI18n", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getIf", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getInclude", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getInputtransferselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getIterator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getLink", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getOptgroup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getOptiontransferselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getParam", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getPassword", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getProperty", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getPush", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getRadio", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getReset", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getScript", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getSelect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getSet", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getSubmit", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getTextarea", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getTextfield", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getUpdownselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "StrutsModels", true, "getUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SubmitModel", true, "SubmitModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SubmitModel", true, "SubmitModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "SubmitModel", true, "SubmitModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TagModel", true, "TagModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TagModel", true, "TagModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TagModel", true, "TagModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextAreaModel", true, "TextAreaModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextAreaModel", true, "TextAreaModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextAreaModel", true, "TextAreaModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextFieldModel", true, "TextFieldModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextFieldModel", true, "TextFieldModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextFieldModel", true, "TextFieldModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextModel", true, "TextModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextModel", true, "TextModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TextModel", true, "TextModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TokenModel", true, "TokenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TokenModel", true, "TokenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "TokenModel", true, "TokenModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "URLModel", true, "URLModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "URLModel", true, "URLModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "URLModel", true, "URLModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "UpDownSelectModel", true, "UpDownSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "UpDownSelectModel", true, "UpDownSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker.tags", "UpDownSelectModel", true, "UpDownSelectModel", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "addSetting", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "buildTemplateModel", "(ValueStack,Object,ServletContext,HttpServletRequest,HttpServletResponse,ObjectWrapper)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "buildTemplateModel", "(ValueStack,Object,ServletContext,HttpServletRequest,HttpServletResponse,ObjectWrapper)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "buildTemplateModel", "(ValueStack,Object,ServletContext,HttpServletRequest,HttpServletResponse,ObjectWrapper)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getConfiguration", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getConfiguration", "(ServletContext)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getConfiguration", "(ServletContext)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getTemplatePath", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "getWrapper", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "init", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "setEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "setTemplateUpdateDelay", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", true, "setThemeTemplateLoader", "(FreemarkerThemeTemplateLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", true, "FreemarkerResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", true, "setFreemarkerManager", "(FreemarkerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", true, "setWriter", "(Writer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerThemeTemplateLoader", true, "getParentTemplateLoader", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerThemeTemplateLoader", true, "init", "(TemplateLoader)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerThemeTemplateLoader", true, "setTemplateEngine", "(TemplateEngine)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerThemeTemplateLoader", true, "setUIThemeExpansionToken", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "PortletFreemarkerResult", true, "PortletFreemarkerResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "PortletFreemarkerResult", true, "getContentType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "PortletFreemarkerResult", true, "setContentType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "PortletFreemarkerResult", true, "setFreemarkerManager", "(FreemarkerManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "ScopesHashModel", "(ObjectWrapper,ServletContext,HttpServletRequest)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "ScopesHashModel", "(ObjectWrapper,ServletContext,HttpServletRequest)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "ScopesHashModel", "(ObjectWrapper,ServletContext,HttpServletRequest,ValueStack)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "ScopesHashModel", "(ObjectWrapper,ServletContext,HttpServletRequest,ValueStack)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "ScopesHashModel", "(ObjectWrapper,ServletContext,HttpServletRequest,ValueStack)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "putUnlistedModel", "(String,TemplateModel)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.freemarker", "ScopesHashModel", true, "putUnlistedModel", "(String,TemplateModel)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", true, "getParams", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", true, "setValueStackFactory", "(ValueStackFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxpResult", true, "setGxpName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "GxpInstance", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "GxpResult", true, "setContainer", "(Container)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "Param", true, "getDefaultValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "Param", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.gxp", "Param", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "JasperReportsResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "getConnection", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "getExportParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "getImageServletUrl", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "getReportParameters", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setConnection", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setContentDisposition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setDataSource", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setDelimiter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setDocumentName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setExportParameters", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setImageServletUrl", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setNotExcludedAcceptedPatterns", "(NotExcludedAcceptedPatternsChecker)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setReportParameters", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", true, "setTimeZone", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "ValueStackDataSource", true, "ValueStackDataSource", "(ValueStack,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "ValueStackDataSource", true, "ValueStackDataSource", "(ValueStack,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "ValueStackShadowMap", true, "ValueStackShadowMap", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "ValueStackShadowMap", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "add", "(String,String,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addDefaultToEmpty", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addDefaultToEmpty", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addDefaultToEmpty", "(String,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addDefaultToEmpty", "(String,Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addDefaultToEmpty", "(String,Object,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfExists", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfExists", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfExists", "(String,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfExists", "(String,Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfExists", "(String,Object,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfTrue", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Attributes", true, "addIfTrue", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.struts2.views.java", "JavaTemplateEngine", true, "setDefaultTemplateType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "JavaTemplateEngine", true, "setTemplateEngineManager", "(TemplateEngineManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "characters", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "characters", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "end", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "setNext", "(TagHandler)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "setup", "(TemplateRenderingContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "start", "(String,Attributes)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "start", "(String,Attributes)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", true, "start", "(String,Attributes)", "", "Argument[this]", "Argument[1].Element", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandlerFactory", true, "create", "(TagHandler)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.java", "Theme", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "IteratorGeneratorTag", true, "setConverter", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "IteratorGeneratorTag", true, "setCount", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "IteratorGeneratorTag", true, "setSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "IteratorGeneratorTag", true, "setVal", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "IteratorGeneratorTag", true, "setVar", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SortIteratorTag", true, "setComparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SortIteratorTag", true, "setSource", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SortIteratorTag", true, "setVar", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SubsetIteratorTag", true, "setCount", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SubsetIteratorTag", true, "setDecider", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SubsetIteratorTag", true, "setSource", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SubsetIteratorTag", true, "setStart", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.iterator", "SubsetIteratorTag", true, "setVar", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractClosingTag", true, "setOpenTemplate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleDisabled", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleEmptyOption", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleHeaderKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleHeaderValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleListKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleListValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleMultiple", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnblur", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnchange", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOndblclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnfocus", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnkeydown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnkeypress", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnkeyup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnmousedown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnmousemove", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnmouseout", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnmouseover", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnmouseup", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleOnselect", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleSize", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getDoubleValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "getFormName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleAccesskey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnblur", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnchange", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOndblclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnfocus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnkeydown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnkeypress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnkeyup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnmousedown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnmousemove", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnmouseout", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnmouseover", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnmouseup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleOnselect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setDoubleValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setFormName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractDoubleListTag", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListLabelKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractListTag", true, "setListValueKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setAccesskey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setCssErrorClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setCssErrorStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setErrorPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setJavascriptTooltip", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setLabelPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setLabelSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnblur", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnchange", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOndblclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnfocus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnkeydown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnkeypress", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnkeyup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnmousedown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnmousemove", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnmouseout", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnmouseover", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnmouseup", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setOnselect", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setRequiredLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setRequiredPosition", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTabindex", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTemplate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTemplateDir", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTooltip", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTooltipConfig", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTooltipCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTooltipDelay", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setTooltipIconPath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AbstractUITag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setEncode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setEscapeAmp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setEscapeHtmlBody", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setForceAddSchemeHostAndPort", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setHref", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setIncludeContext", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setPortletUrlType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "AnchorTag", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "CheckboxTag", true, "setFieldValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "CheckboxTag", true, "setSubmitUnchecked", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ComboBoxTag", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "DateTextFieldTag", true, "setFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FieldErrorTag", true, "setFieldName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FileTag", true, "setAccept", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FileTag", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setAcceptcharset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setEnctype", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setFocusElement", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setOnreset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setOnsubmit", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setTarget", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setValidate", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getAddLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getAllowRemoveAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getAllowUpDown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getButtonCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getButtonCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getHeaderKey", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getHeaderValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getLeftTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getMultiple", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getRemoveAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getRemoveLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getRightTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getSize", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "getUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setAddLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setAllowRemoveAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setAllowUpDown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setButtonCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setButtonCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setLeftTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setRemoveAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setRemoveLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setRightTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "InputTransferSelectTag", true, "setUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LabelTag", true, "setFor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setAs", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setCrossorigin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setHref", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setHreflang", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setMedia", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setReferrerpolicy", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setRel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setSizes", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "LinkTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OgnlTool", true, "setOgnlUtil", "(OgnlUtil)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setDisabled", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setList", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setListCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setListCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setListKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setListTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptGroupTag", true, "setListValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddAllToLeftLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddAllToLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddAllToRightLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddAllToRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddToLeftLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddToLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddToRightLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAddToRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowAddAllToLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowAddAllToRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowAddToLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowAddToRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowSelectAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowUpDownOnLeft", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getAllowUpDownOnRight", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getButtonCssClass", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getButtonCssStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getLeftDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getLeftTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getLeftUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getRightDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getRightTitle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getRightUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getSelectAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getSelectAllOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getUpDownOnLeftOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "getUpDownOnRightOnclick", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddAllToLeftLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddAllToLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddAllToRightLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddAllToRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddToLeftLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddToLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddToRightLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAddToRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowAddAllToLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowAddAllToRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowAddToLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowAddToRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowSelectAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowUpDownOnLeft", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setAllowUpDownOnRight", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setButtonCssClass", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setButtonCssStyle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setLeftDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setLeftTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setLeftUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setRightDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setRightTitle", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setRightUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setSelectAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setSelectAllOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setUpDownOnLeftOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OptionTransferSelectTag", true, "setUpDownOnRightOnclick", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "PasswordTag", true, "setShow", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "PasswordTag", true, "setShowPassword", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ResetTag", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ResetTag", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ResetTag", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ResetTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setAsync", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setCharset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setCrossorigin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setDefer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setIntegrity", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setNomodule", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setReferrerpolicy", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ScriptTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SelectTag", true, "setEmptyOption", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SelectTag", true, "setHeaderKey", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SelectTag", true, "setHeaderValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SelectTag", true, "setMultiple", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SelectTag", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", true, "getType", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", true, "setSrc", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextFieldTag", true, "setMaxlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextFieldTag", true, "setReadonly", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextFieldTag", true, "setSize", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextFieldTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setCols", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setMaxlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setMinlength", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setReadonly", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setRows", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "TextareaTag", true, "setWrap", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getAllowMoveDown", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getAllowMoveUp", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getAllowSelectAll", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getMoveDownLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getMoveUpLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "getSelectAllLabel", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setAllowMoveDown", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setAllowMoveUp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setAllowSelectAll", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setMoveDownLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setMoveUpLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "UpDownSelectTag", true, "setSelectAllLabel", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "BeanTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ComponentTagSupport", true, "getBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ComponentTagSupport", true, "getBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ComponentTagSupport", true, "getBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ComponentTagSupport", true, "getComponent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ContextBeanTag", true, "setVar", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "DateTag", true, "setFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "DateTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "DateTag", true, "setTimezone", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ElseIfTag", true, "setTest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "I18nTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IfTag", true, "setTest", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IncludeTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", true, "IteratorStatus", "(StatusState)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorTag", true, "setBegin", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorTag", true, "setEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorTag", true, "setStatus", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorTag", true, "setStep", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", true, "setCurrency", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", true, "setRoundingMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", true, "setType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ParamTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "ParamTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", true, "setDefault", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", true, "setDefaultValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "PushTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "SetTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "SetTag", true, "setScope", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "SetTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "TagUtils", true, "getStack", "(PageContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "TextTag", true, "setName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setAction", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setAnchor", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setEncode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setEscapeAmp", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setForceAddSchemeHostAndPort", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setIncludeContext", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setIncludeParams", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setMethod", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setNamespace", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setPortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setPortletUrlType", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setScheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setValue", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.jsp", "URLTag", true, "setWindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.tiles", "PortletTilesResult", true, "PortletTilesResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.tiles", "TilesResult", true, "TilesResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "ContextUtil", true, "getStandardContext", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "ContextUtil", true, "getStandardContext", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "ContextUtil", true, "getStandardContext", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "ContextUtil", true, "getTemplateSuffix", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "DefaultUrlHelper", true, "setQueryStringBuilder", "(QueryStringBuilder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "ResourceUtil", true, "getResourceBase", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[3].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.util", "UrlHelper", true, "buildUrl", "(String,HttpServletRequest,HttpServletResponse,Map,String,boolean,boolean,boolean,boolean)", "", "Argument[4]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity.result", "VelocityResult", true, "VelocityResult", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity.result", "VelocityResult", true, "setDefaultEncoding", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity.result", "VelocityResult", true, "setVelocityManager", "(VelocityManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity.template", "VelocityTemplateEngine", true, "setVelocityManager", "(VelocityManager)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "StrutsVelocityContext", true, "StrutsVelocityContext", "(List,ValueStack)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "StrutsVelocityContext", true, "StrutsVelocityContext", "(List,ValueStack)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "StrutsVelocityContext", true, "StrutsVelocityContext", "(ValueStack)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "StrutsVelocityContext", true, "StrutsVelocityContext", "(VelocityContext[],ValueStack)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "createContext", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "getToolboxManager", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "getVelocityEngine", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "getVelocityProperties", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "loadConfiguration", "(ServletContext)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "loadConfiguration", "(ServletContext)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "setChainedContexts", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "setCustomConfigFile", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "setObjectFactory", "(ObjectFactory)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "setToolBoxLocation", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", true, "setVelocityProperties", "(Properties)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", true, "VelocityStrutsUtil", "(VelocityEngine,Context,ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", true, "VelocityStrutsUtil", "(VelocityEngine,Context,ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", true, "VelocityStrutsUtil", "(VelocityEngine,Context,ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", true, "VelocityStrutsUtil", "(VelocityEngine,Context,ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[3]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", true, "VelocityStrutsUtil", "(VelocityEngine,Context,ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[4]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2.views", "DefaultTagLibrary", true, "getFreemarkerModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views", "DefaultTagLibrary", true, "getFreemarkerModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views", "DefaultTagLibrary", true, "getFreemarkerModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views", "TagLibraryModelProvider", true, "getModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views", "TagLibraryModelProvider", true, "getModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2.views", "TagLibraryModelProvider", true, "getModels", "(ValueStack,HttpServletRequest,HttpServletResponse)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "JSPServletConfig", true, "JSPServletConfig", "(ServletContext)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "RequestUtils", true, "getServletPath", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "RequestUtils", true, "getUri", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", true, "getValueStack", "(HttpServletRequest)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "ServletCache", true, "get", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "ServletCache", true, "get", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "ServletCache", true, "launderThrowable", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(String,Throwable,Object)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.struts2", "StrutsException", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["actions.osgi", "BundlesAction", "getBundles", "()", "summary", "df-generated"] + - ["actions.osgi", "HelloWorldAction", "getSimpleMessage", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", "getAllowedMethods", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", "isAllowedMethod", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "ActionConfig", "isStrictMethodInvocation", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", "isAllowed", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", "isStrictMethodInvocation", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "AllowedMethods", "list", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", "isNeedsRefresh", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig$Builder", "isStrictMethodInvocation", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", "isAbstract", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", "isNeedsRefresh", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.entities", "PackageConfig", "isStrictMethodInvocation", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "AbstractMatcher", "addPattern", "(String,Object,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "AbstractMatcher", "freeze", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "AbstractMatcher", "match", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "MockConfiguration", "buildRuntimeConfiguration", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.impl", "MockConfiguration", "selfRegister", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "CycleDetector", "containsCycle", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", "addEdge", "(Object,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", "edgeExists", "(Object,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", "isEmpty", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", "removeEdge", "(Object,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "DirectedGraph", "size", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "XmlDocConfigurationProvider", "(Document[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "iterateChildren", "(Node,Consumer)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "iterateChildrenByTagName", "(Element,String,Consumer)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "iterateElementChildren", "(Document,Consumer)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "iterateElementChildren", "(Node,Consumer)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlDocConfigurationProvider", "setThrowExceptionOnDuplicateBeans", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlHelper", "getContent", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlHelper", "getLoadOrder", "(Document)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config.providers", "XmlHelper", "getParams", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", "destroy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", "getRuntimeConfiguration", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", "rebuildRuntimeConfiguration", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "Configuration", "reloadContainer", "(List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "clearContainerProviders", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "conditionalReload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "destroyAndReload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "destroyConfiguration", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "reload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ConfigurationManager", "removeContainerProvider", "(ContainerProvider)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", "destroy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", "init", "(Configuration)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", "needsReload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "ContainerProvider", "register", "(ContainerBuilder,LocatableProperties)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "FileManagerFactoryProvider", "FileManagerFactoryProvider", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "PackageProvider", "init", "(Configuration)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "PackageProvider", "loadPackages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.config", "PackageProvider", "needsReload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.annotations", "ConversionRule", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.annotations", "ConversionType", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "ConversionData", "getToClass", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "ConversionData", "setToClass", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "bigDecValue", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "bigIntValue", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "booleanValue", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "doubleValue", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "enumValue", "(Class,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "getTypeConverter", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "DefaultTypeConverter", "longValue", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", "buildConverterFilename", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", "getConversionErrorMessage", "(String,Class,ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", "registerConverter", "(String,TypeConverter)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", "registerConverterNotFound", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkConverter", "setReloadingConfigs", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkList", "XWorkList", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion.impl", "XWorkList", "XWorkList", "(Class,int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ConversionFileProcessor", "process", "(Map,Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ConversionPropertiesProcessor", "process", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ConversionPropertiesProcessor", "processRequired", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "NullHandler", "nullMethodResult", "(Map,Object,String,Object[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ObjectTypeDeterminer", "getElementClass", "(Class,String,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ObjectTypeDeterminer", "getKeyClass", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ObjectTypeDeterminer", "getKeyProperty", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "ObjectTypeDeterminer", "shouldCreateIfNew", "(Class,String,Object,String,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverter", "convertValue", "(Map,Object,Member,String,Object,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterCreator", "createTypeConverter", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterCreator", "createTypeConverter", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", "addNoMapping", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", "containsDefaultMapping", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", "containsNoMapping", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.conversion", "TypeConverterHolder", "containsUnknownMapping", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.factory", "ConverterFactory", "buildConverter", "(Class,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.factory", "ResultFactory", "buildResult", "(ResultConfig,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.factory", "UnknownHandlerFactory", "buildUnknownHandler", "(String,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.factory", "ValidatorFactory", "buildValidator", "(String,Map,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject.util", "ReferenceCache", "ReferenceCache", "(ReferenceType,ReferenceType)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject.util", "ReferenceCache", "of", "(ReferenceType,ReferenceType,Function)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject.util", "ReferenceMap", "ReferenceMap", "(ReferenceType,ReferenceType)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "getInstance", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "getInstance", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "getInstanceNames", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "inject", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "inject", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "removeScopeStrategy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Container", "setScopeStrategy", "(Strategy)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", "contains", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", "contains", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "ContainerBuilder", "setAllowDuplicates", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Context", "getScopeStrategy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Context", "getType", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Initializable", "init", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.inject", "Scope", "fromString", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AbstractInterceptor", "setDisabled", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", "setAcceptParamNames", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", "setDevMode", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "AliasInterceptor", "setExcludeParams", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", "setCopyErrors", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", "setCopyFieldErrors", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ChainingInterceptor", "setCopyMessages", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ConditionalInterceptor", "shouldIntercept", "(ActionInvocation)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionHolder", "getExceptionStack", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", "getDepth", "(String,Throwable)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", "isLogEnabled", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ExceptionMappingInterceptor", "setLogEnabled", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", "destroy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", "init", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "Interceptor", "intercept", "(ActionInvocation)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptorUtil", "applyMethod", "(Set,Set,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "MethodFilterInterceptorUtil", "applyMethod", "(String,String,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ModelDrivenInterceptor", "setRefreshModelBeforeResult", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", "isDefaultBlock", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParameterFilterInterceptor", "setDefaultBlock", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "isOrdered", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setAcceptParamNames", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setAcceptedValuePatterns", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setDevMode", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setExcludeParams", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setExcludedValuePatterns", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setOrdered", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ParametersInterceptor", "setParamNameMaxLength", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PreResultListener", "beforeResult", "(ActionInvocation,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PrefixMethodInvocationUtil", "getPrefixedMethod", "(String[],String,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PrefixMethodInvocationUtil", "invokePrefixMethod", "(ActionInvocation,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PrepareInterceptor", "setAlwaysInvokePrepare", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "PrepareInterceptor", "setFirstCallPrepareDo", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", "setDevMode", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", "setMerge", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", "setOverwrite", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "StaticParametersInterceptor", "setParse", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "addActionError", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "addActionMessage", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "addFieldError", "(String,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "getActionErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "getActionMessages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "getFieldErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "hasActionErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "hasActionMessages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "hasErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "hasFieldErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "setActionErrors", "(Collection)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "setActionMessages", "(Collection)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "ValidationAware", "setFieldErrors", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.interceptor", "WithLazyParams$LazyParamInjector", "LazyParamInjector", "(ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", "isExecutedCalled", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", "prepare", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockActionProxy", "setMethodSpecified", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockInterceptor", "isExecuted", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "getElementClass", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "getKeyClass", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "isShouldCreateIfNew", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "setElementClass", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "setKeyClass", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockObjectTypeDeterminer", "setShouldCreateIfNew", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.mock", "MockResult", "setFoo", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl.accessor", "XWorkListPropertyAccessor", "setAutoGrowCollectionLimit", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ErrorMessageBuilder", "create", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", "getLastClassAccessed", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "ObjectProxy", "setLastClassAccessed", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlDefaultCache", "OgnlDefaultCache", "(int,int,float)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlLRUCache", "OgnlLRUCache", "(int,int,float)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "OgnlUtil", "(ExpressionCacheFactory,BeanInfoCacheFactory)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "beanInfoCacheSize", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "callMethod", "(String,Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "clearBeanInfoCache", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "clearExpressionCache", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "clearRuntimeCache", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "copy", "(Object,Object,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "copy", "(Object,Object,Map,Collection,Collection)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "copy", "(Object,Object,Map,Collection,Collection,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "expressionCacheSize", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "getBeanMap", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "getPropertyDescriptors", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "getPropertyDescriptors", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "getValue", "(String,Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "getValue", "(String,Map,Object,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "isDisallowProxyMemberAccess", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setDevModeExcludedPackageExemptClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setExcludedPackageExemptClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperties", "(Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperties", "(Map,Object,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperties", "(Map,Object,Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperties", "(Map,Object,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperty", "(String,Object,Object,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setProperty", "(String,Object,Object,Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", "setValue", "(String,Map,Object,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", "SecurityMemberAccess", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.ognl", "SecurityMemberAccess", "setDisallowProxyMemberAccess", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker$IsAccepted", "isAccepted", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker", "isAccepted", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker", "setAcceptedPatterns", "(Set)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker", "setAcceptedPatterns", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "AcceptedPatternsChecker", "setAcceptedPatterns", "(String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "DefaultAcceptedPatternsChecker", "DefaultAcceptedPatternsChecker", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "DefaultExcludedPatternsChecker", "setAdditionalExcludePatterns", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker$IsExcluded", "isExcluded", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker$IsExcluded", "no", "(Set)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker$IsExcluded", "yes", "(Pattern)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker", "isExcluded", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker", "setExcludedPatterns", "(Set)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker", "setExcludedPatterns", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "ExcludedPatternsChecker", "setExcludedPatterns", "(String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker$IsAllowed", "isAllowed", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.security", "NotExcludedAcceptedPatternsChecker", "isAllowed", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring.interceptor", "ActionAutowiringInterceptor", "setAutowireStrategy", "(Integer)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "getAutowireStrategy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "setAlwaysRespectAutowireStrategy", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "setApplicationContextPath", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "setAutowireStrategy", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "setEnableAopSupport", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.spring", "SpringObjectFactory", "setUseClassCache", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "JarResourceStore", "copy", "(InputStream,OutputStream)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ReloadingClassLoader", "reload", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ReloadingClassLoader", "removeResourceStore", "(ResourceStore)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ResourceStore", "read", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.classloader", "ResourceStore", "write", "(String,byte[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$Annotatable", "Annotatable", "(AnnotatedElement)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$AnnotationInfo", "AnnotationInfo", "(Annotation)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$AnnotationInfo", "AnnotationInfo", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder$ClassInfo", "get", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findAnnotatedClasses", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findAnnotatedConstructors", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findAnnotatedFields", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findAnnotatedMethods", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findClasses", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findClasses", "(Test)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "findClassesInPackage", "(String,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassFinder", "isAnnotationPresent", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterface", "getParent", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterface", "getResource", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterface", "getResourceAsStream", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterface", "getResources", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ClassLoaderInterface", "loadClass", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "find", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAll", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAllClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAllImplementations", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAllProperties", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAllStrings", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAvailableClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAvailableImplementations", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAvailableProperties", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findAvailableStrings", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findClass", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findImplementation", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findPackages", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findPackagesMap", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findProperties", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "findString", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "getResourcesMap", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAllClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAllImplementations", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAllProperties", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAllStrings", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAvailableClasses", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAvailableImplementations", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAvailableProperties", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "ResourceFinder", "mapAvailableStrings", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.finder", "UrlSet", "UrlSet", "(ClassLoaderInterface)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "Revision", "build", "(URL)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.fs", "Revision", "needsReloading", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Location", "getColumnNumber", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Location", "getLineNumber", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "Location", "getSnippet", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getColumn", "(Attributes)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getColumn", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getLine", "(Attributes)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getLine", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getLocationString", "(Attributes)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getLocationString", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getURI", "(Attributes)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "getURI", "(Element)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationAttributes", "remove", "(Element,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", "addFinder", "(LocationFinder)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", "isKnown", "(Location)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.location", "LocationUtils", "isUnknown", "(Location)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextFactory", "createDefaultContext", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "clear", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "clearCurrentPropertyPath", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "getLastBeanClassAccessed", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "isCreatingNullObjects", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "isDenyMethodExecution", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "isGettingByKeyProperty", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "isReportingConversionErrors", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setCreatingNullObjects", "(Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setDenyMethodExecution", "(Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setGettingByKeyProperty", "(Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setLastBeanClassAccessed", "(Map,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setReportingConversionErrors", "(Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "setSetMap", "(Map,Map,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionContextState", "updateCurrentPropertyPath", "(Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionExceptionHandler", "handle", "(ReflectionException)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "copy", "(Object,Object,Map,Collection,Collection)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "copy", "(Object,Object,Map,Collection,Collection,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getBeanMap", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getField", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getGetMethod", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getPropertyDescriptor", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getPropertyDescriptors", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getSetMethod", "(Class,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "getValue", "(String,Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setProperties", "(Map,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setProperties", "(Map,Object,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setProperties", "(Map,Object,Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setProperty", "(String,Object,Object,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setProperty", "(String,Object,Object,Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProvider", "setValue", "(String,Map,Object,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util.reflection", "ReflectionProviderFactory", "getInstance", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "addAllFields", "(Class,Class,List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "addAllInterfaces", "(Class,List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "addAllMethods", "(Class,Class,List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "findAnnotation", "(Class,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "findAnnotations", "(Class,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "isAnnotatedBy", "(AnnotatedElement,Class[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "AnnotationUtils", "resolvePropertyName", "(Method)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "getResource", "(String,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "getResourceAsStream", "(String,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "getResources", "(String,Class,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "loadClass", "(String,Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "printClassLoader", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassLoaderUtil", "printClassLoader", "(ClassLoader)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClassPathFinder", "findMatches", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ClearableValueStack", "clearContextValues", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "CompoundRoot", "CompoundRoot", "(List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "CompoundRoot", "cutStack", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper$DOMBuilder", "getDocument", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper", "parse", "(InputSource)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "DomHelper", "parse", "(InputSource,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "Evaluated", "isDefined", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "PropertiesReader", "(Reader)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "PropertiesReader", "(Reader,char)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "contains", "(char[],char)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "nextProperty", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "readProperty", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "unescapeJava", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "PropertiesReader", "unescapeJava", "(Writer,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ProxyUtil", "isProxy", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ProxyUtil", "isProxyMember", "(Member,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ProxyUtil", "ultimateTargetClass", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$AnnotatedWith", "AnnotatedWith", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$AnnotatedWith", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$IsA", "IsA", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$IsA", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$Test", "doesMatchClass", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$Test", "doesMatchResource", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$Test", "matches", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil$Test", "matches", "(URL)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "find", "(Test,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "findAnnotated", "(Class,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "findImplementations", "(Class,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "findInPackage", "(Test,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "findNamedResource", "(String,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ResolverUtil", "findSuffix", "(String,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "StrutsLocalizedTextProvider", "clearDefaultResourceBundles", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil$ParsedValueEvaluator", "evaluate", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", "translateVariables", "(String,ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", "translateVariables", "(String,ValueStack,ParsedValueEvaluator)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", "translateVariables", "(char,String,ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", "translateVariablesCollection", "(String,ValueStack,boolean,ParsedValueEvaluator)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", "translateVariablesCollection", "(char[],String,ValueStack,boolean,ParsedValueEvaluator,int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "set", "(String,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "setDefaultType", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "setParameter", "(String,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "setValue", "(String,Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "setValue", "(String,Object,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "ValueStack", "size", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "WildcardUtil", "needsBackslashToBeLiteralInRegex", "(char)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "XWorkTestCaseHelper", "setUp", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.util", "XWorkTestCaseHelper", "tearDown", "(ConfigurationManager)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.annotations", "ValidatorType", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "ConditionalVisitorFieldValidator", "validateExpression", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "getMaxExclusive", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "getMaxInclusive", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "getMinExclusive", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "getMinInclusive", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "setMaxExclusive", "(Double)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "setMaxInclusive", "(Double)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "setMinExclusive", "(Double)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "DoubleRangeFieldValidator", "setMinInclusive", "(Double)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", "isCaseSensitive", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", "isTrimed", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", "setCaseSensitive", "(Boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RegexFieldValidator", "setTrim", "(Boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RepopulateConversionErrorFieldValidatorSupport", "isRepopulateField", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RepopulateConversionErrorFieldValidatorSupport", "repopulateField", "(Object)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RepopulateConversionErrorFieldValidatorSupport", "setRepopulateField", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RequiredStringValidator", "isTrim", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RequiredStringValidator", "setTrim", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "RequiredStringValidator", "setTrimExpression", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "getMaxLength", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "getMinLength", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "isTrim", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "setMaxLength", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "setMinLength", "(int)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "StringLengthFieldValidator", "setTrim", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "URLValidator", "getUrlRegex", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "URLValidator", "setUrlRegex", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator", "isAppendPrefix", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator.validators", "VisitorFieldValidator", "setAppendPrefix", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "AnnotationValidationConfigurationBuilder", "buildAnnotationClassValidatorConfigs", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultActionValidatorManager", "setFileManagerFactory", "(FileManagerFactory)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DefaultActionValidatorManager", "setReloadingConfigs", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "DelegatingValidatorContext", "DelegatingValidatorContext", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ShortCircuitableValidator", "isShortCircuit", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ShortCircuitableValidator", "setShortCircuit", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", "isValidateAnnotatedMethodOnly", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", "setAlwaysInvokeValidate", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", "setDeclarative", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", "setProgrammatic", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidationInterceptor", "setValidateAnnotatedMethodOnly", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorConfig", "isShortCircuit", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFileParser", "parseActionValidatorConfigs", "(ValidatorFactory,InputStream,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2.validator", "ValidatorFileParser", "parseValidatorDefinitions", "(Map,InputStream,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "Action", "execute", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionChainResult", "getChainHistory", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "bind", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "bind", "(ActionContext)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "clear", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "containsValueStack", "(Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "getContext", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "getInstance", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionContext", "of", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "addPreResultListener", "(PreResultListener)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "getInvocationContext", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "getProxy", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "getResult", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "getResultCode", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "getStack", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "init", "(ActionProxy)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "invoke", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "invokeActionOnly", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "isExecuted", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "setActionEventListener", "(ActionEventListener)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionInvocation", "setResultCode", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", "getExecuteResult", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", "isMethodSpecified", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionProxy", "setExecuteResult", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "clearActionErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "clearErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "clearErrorsAndMessages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "clearFieldErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "clearMessages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "input", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ActionSupport", "pause", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "AsyncManager", "hasAsyncActionResult", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "AsyncManager", "invokeAsyncAction", "(Callable)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "DefaultActionProxyFactory", "toString", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "fileNeedsReloading", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "fileNeedsReloading", "(URL)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "internal", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "loadFile", "(URL)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "monitorFile", "(URL)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "setReloadingConfigs", "(boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManager", "support", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "FileManagerFactory", "setReloadingConfigs", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocaleProvider", "getLocale", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocaleProvider", "isValidLocale", "(Locale)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocaleProvider", "isValidLocaleString", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocaleProviderFactory", "createLocaleProvider", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "addDefaultResourceBundle", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findDefaultText", "(String,Locale,Object[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(Class,String,Locale)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(Class,String,Locale,String,Object[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(Class,String,Locale,String,Object[],ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(ResourceBundle,String,Locale)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(ResourceBundle,String,Locale,String,Object[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "LocalizedTextProvider", "findText", "(ResourceBundle,String,Locale,String,Object[],ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildAction", "(String,String,ActionConfig,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildBean", "(Class,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildBean", "(String,Map,boolean)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildConverter", "(Class,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildInterceptor", "(InterceptorConfig,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildResult", "(ResultConfig,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildUnknownHandler", "(String,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "buildValidator", "(String,Map,Map)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "getClassInstance", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ObjectFactory", "isNoArgConstructorRequired", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "Preparable", "prepare", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ResourceBundleTextProvider", "setClazz", "(Class)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "Result", "execute", "(ActionInvocation)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String,List)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String,List,ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String,String[],ValueStack)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getText", "(String,String[])", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "getTexts", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProvider", "hasKey", "(String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "TextProviderSupport", "setLocaleProviderFactory", "(LocaleProviderFactory)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandler", "handleUnknownActionMethod", "(Object,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandler", "handleUnknownResult", "(ActionContext,String,ActionConfig,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandlerManager", "handleUnknownResult", "(ActionContext,String,ActionConfig,String)", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "UnknownHandlerManager", "hasUnknownHandlers", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "Validateable", "validate", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ValidationAwareSupport", "clearActionErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ValidationAwareSupport", "clearErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ValidationAwareSupport", "clearErrorsAndMessages", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ValidationAwareSupport", "clearFieldErrors", "()", "summary", "df-generated"] + - ["com.opensymphony.xwork2", "ValidationAwareSupport", "clearMessages", "()", "summary", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "BeanValidationInterceptor", "setConvertToUtf8", "(String)", "summary", "df-generated"] + - ["org.apache.struts.beanvalidation.validation.interceptor", "BeanValidationManager", "getValidator", "()", "summary", "df-generated"] + - ["org.apache.struts2.action", "ServletRequestAware", "withServletRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.action", "ServletResponseAware", "withServletResponse", "(HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.async", "AsyncAction", "getTimeout", "()", "summary", "df-generated"] + - ["org.apache.struts2.compiler", "MemoryJavaFileObject", "MemoryJavaFileObject", "(String,Kind)", "summary", "df-generated"] + - ["org.apache.struts2.components.date", "DateFormatter", "format", "(TemporalAccessor,String)", "summary", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngine", "renderTemplate", "(TemplateRenderingContext)", "summary", "df-generated"] + - ["org.apache.struts2.components.template", "TemplateEngineManager", "getTemplateEngine", "(Template,String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", "setExecuteResult", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", "setFlush", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", "setIgnoreContextParams", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionComponent", "setRethrowException", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionError", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ActionMessage", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Anchor", "setEncode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Anchor", "setEscapeAmp", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Anchor", "setEscapeHtmlBody", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Anchor", "setForceAddSchemeHostAndPort", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Anchor", "setIncludeContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ClosingUIBean", "getDefaultOpenTemplate", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "addParameter", "(String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "end", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "escapeHtmlBody", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "getPerformClearTagStateForTagPoolingServers", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "isValidTagAttribute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "setEscapeHtmlBody", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "setPerformClearTagStateForTagPoolingServers", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "setThrowExceptionsOnELFailure", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "start", "(Writer)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Component", "usesBody", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "ContextBean", "setVar", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Date", "isNice", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Date", "setNice", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "FieldError", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "FieldError", "setFieldName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Form", "getValidators", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Form", "setIncludeContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "GenericUIBean", "contains", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Include", "setUseResponseEncoding", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", "contains", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.components", "ListUIBean", "setThrowExceptionOnNullValueAttribute", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "getMaximumFractionDigits", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "getMaximumIntegerDigits", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "getMinimumFractionDigits", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "getMinimumIntegerDigits", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "isGroupingUsed", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "isParseIntegerOnly", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setGroupingUsed", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setMaximumFractionDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setMaximumIntegerDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setMinimumFractionDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setMinimumIntegerDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Number", "setParseIntegerOnly", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "OptGroup", "setContainer", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Param", "setSuppressEmptyParameters", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Property", "setEscapeCsv", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Property", "setEscapeHtml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Property", "setEscapeJavaScript", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Property", "setEscapeXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Set", "setTrimBody", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Submit", "setEscapeHtmlBody", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Text", "setEscapeCsv", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Text", "setEscapeHtml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Text", "setEscapeJavaScript", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "Text", "setEscapeXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UIBean", "addFormParameter", "(String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UIBean", "evaluateExtraParams", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UIBean", "evaluateParams", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UIBean", "getDefaultTemplate", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "URL", "setEncode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "URL", "setEscapeAmp", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "URL", "setForceAddSchemeHostAndPort", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "URL", "setIncludeContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "determineNamespace", "(String,ValueStack,HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "findString", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "isEncode", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "isEscapeAmp", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "isForceAddSchemeHostAndPort", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "isIncludeContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "isPutInContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "putInContext", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "setEncode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "setEscapeAmp", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "setForceAddSchemeHostAndPort", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlProvider", "setIncludeContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlRenderer", "beforeRenderUrl", "(UrlProvider)", "summary", "df-generated"] + - ["org.apache.struts2.components", "UrlRenderer", "renderFormUrl", "(Form)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "BeanConfig", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "getClazz", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "getScope", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "isOnlyStatic", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanConfig", "isOptional", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanSelectionConfig", "BeanSelectionConfig", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "BeanSelectionConfig", "getClazz", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getConfigurationXmlReload", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getDevMode", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getDisableRequestAttributeValueStackLookup", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getDisallowProxyMemberAccess", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getDispatcherParametersWorkaround", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getElThrowExceptionOnFailure", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getEnableDynamicMethodInvocation", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getEnableSlashesInActionNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getFreemarkerBeanwrapperCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getFreemarkerMruMaxStrongSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getFreemarkerWrapperAltMap", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getHandleException", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getI18nReload", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMapperActionPrefixCrossNamespaces", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMapperActionPrefixEnabled", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMapperAlwaysSelectFullNamespace", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartBufferSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartEnabled", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartMaxFileSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartMaxFiles", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartMaxSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getMultipartMaxStringLength", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getObjectFactorySpringAutoWireAlwaysRespect", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getObjectFactorySpringEnableAopSupport", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getObjectFactorySpringUseClassCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getOgnlAllowStaticFieldAccess", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getOgnlAutoGrowthCollectionLimit", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getOgnlEnableEvalExpression", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getOgnlEnableExpressionCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getOgnlLogMissingProperties", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getServeStatic", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getServeStaticBrowserCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getUrlHttpPort", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getUrlHttpsPort", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "getXsltNocache", "()", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setAcceptedPatternsChecker", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setActionProxyFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setActionValidatorManager", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setBeaninfoCacheFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConfigurationXmlReload", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setContentTypeMatcher", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterAnnotationProcessor", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterArray", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterCollection", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterCreator", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterDate", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterFileProcessor", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterHolder", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterNumber", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterPropertiesProcessor", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setConverterString", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setDevMode", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setDisableRequestAttributeValueStackLookup", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setDisallowProxyMemberAccess", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setDispatcherErrorHandler", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setDispatcherParametersWorkaround", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setElThrowExceptionOnFailure", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setEnableDynamicMethodInvocation", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setEnableSlashesInActionNames", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setExcludedPatternsChecker", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setExpressionCacheFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setExpressionParser", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setFreemarkerBeanwrapperCache", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setFreemarkerManagerClassname", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setFreemarkerMruMaxStrongSize", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setFreemarkerWrapperAltMap", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setHandleException", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setI18nReload", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setLocaleProviderFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setLocalizedTextProvider", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMapperActionPrefixCrossNamespaces", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMapperActionPrefixEnabled", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMapperAlwaysSelectFullNamespace", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMapperClass", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartBufferSize", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartEnabled", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartMaxFileSize", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartMaxFiles", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartMaxSize", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartMaxStringLength", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setMultipartParser", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setNotExcludedAcceptedPatternsChecker", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryActionFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryConverterFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryInterceptorFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryResultFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactorySpringAutoWireAlwaysRespect", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactorySpringEnableAopSupport", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactorySpringUseClassCache", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryUnknownHandlerFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectFactoryValidatorFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setObjectTypeDeterminer", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setOgnlAllowStaticFieldAccess", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setOgnlAutoGrowthCollectionLimit", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setOgnlEnableEvalExpression", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setOgnlEnableExpressionCache", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setOgnlLogMissingProperties", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setPatternMatcher", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setReflectionContextFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setReflectionProvider", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setServeStatic", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setServeStaticBrowserCache", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setStaticContentLoader", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setTextProviderFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setUnknownHandlerManager", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setUrlHttpPort", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setUrlHttpsPort", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setUrlRenderer", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setValueStackFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setVelocityManagerClassname", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setViewUrlHelper", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setXsltNocache", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config.entities", "ConstantConfig", "setXworkConverter", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config", "Settings", "getLocation", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.config", "Settings", "list", "()", "summary", "df-generated"] + - ["org.apache.struts2.config", "StrutsJavaConfiguration", "beanSelection", "()", "summary", "df-generated"] + - ["org.apache.struts2.config", "StrutsJavaConfigurationProvider", "setThrowExceptionOnDuplicateBeans", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config", "StrutsXmlConfigurationProvider", "StrutsXmlConfigurationProvider", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", "getConfig", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", "getNamespace", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", "index", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ActionNamesAction", "redirect", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ConfigurationHelper", "getActionConfig", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ConfigurationHelper", "getActionNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ConfigurationHelper", "getJarProperties", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ConfigurationHelper", "getNamespaces", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", "index", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ListValidatorsAction", "stripPackage", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowBeansAction$Binding", "isDefault", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowConfigAction", "stripPackage", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowJarsAction", "getJarPoms", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowJarsAction", "getPluginsLoaded", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction$PropertyInfo", "setType", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", "getSelected", "()", "summary", "df-generated"] + - ["org.apache.struts2.config_browser", "ShowValidatorAction", "setSelected", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionAlwaysMapExecute", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionCheckImplementsAction", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionDisableScanning", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionEagerLoading", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionMapAllMatches", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionActionNameLowercase", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionClassesReload", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionEnableSmiInheritance", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionExcludeParentClassLoader", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionPackageLocatorsDisable", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionRedirectToSlash", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "getConventionResultFlatLayout", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionAlwaysMapExecute", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionCheckImplementsAction", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionConfigBuilder", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionDisableScanning", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionEagerLoading", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionMapAllMatches", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionNameBuilder", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionActionNameLowercase", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionClassesReload", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionConventionsService", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionEnableSmiInheritance", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionExcludeParentClassLoader", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionInterceptorMapBuilder", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionPackageLocatorsDisable", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionRedirectToSlash", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionResultFlatLayout", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.convention.config.entities", "ConventionConstantConfig", "setConventionResultMapBuilder", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ActionConfigBuilder", "buildActionConfigs", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ActionConfigBuilder", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ActionConfigBuilder", "needsReload", "()", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ClasspathConfigurationProvider", "ClasspathConfigurationProvider", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ClasspathConfigurationProvider", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ClasspathConfigurationProvider", "setReload", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ClasspathPackageProvider", "ClasspathPackageProvider", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "DefaultActionNameBuilder", "DefaultActionNameBuilder", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "DefaultResultMapBuilder", "setFlatResultLayout", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "InterceptorMapBuilder", "build", "(Class,Builder,String,Action)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setAlwaysMapExecute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setCheckImplementsAction", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setDisableActionScanning", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setDisablePackageLocatorsScanning", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setEagerLoading", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setExcludeParentClassLoader", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setFileManagerFactory", "(FileManagerFactory)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setMapAllMatches", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setReload", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "PackageBasedActionConfigBuilder", "setSlashesInActionNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ReflectionTools", "containsMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ReflectionTools", "getAnnotation", "(Class,String,Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "ReflectionTools", "getClassHierarchy", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.convention", "StringTools", "contains", "(String[],String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.conversion", "StrutsConversionPropertiesProcessor", "loadConversionProperties", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "CompositeActionMapper", "CompositeActionMapper", "(Container,String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", "isSlashesInActionNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", "setAllowActionPrefix", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", "setAllowDynamicMethodCalls", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", "setAlwaysSelectFullNamespace", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.mapper", "DefaultActionMapper", "setSlashesInActionNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setBufferSize", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setLocaleProviderFactory", "(LocaleProviderFactory)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setMaxFileSize", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setMaxFiles", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setMaxSize", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "AbstractMultiPartRequest", "setMaxStringLength", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "cleanUp", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getContentType", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getFile", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getFileNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getFileParameterNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getFilesystemName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getParameter", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getParameterNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "getParameterValues", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequest", "parse", "(HttpServletRequest,String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "cleanUp", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "getContentTypes", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "getFileNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "getFileSystemNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "getFiles", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "MultiPartRequestWrapper", "hasErrors", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", "delete", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", "isFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher.multipart", "UploadedFile", "length", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultDispatcherErrorHandler", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultStaticContentLoader", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultStaticContentLoader", "setServeStaticBrowserCache", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DefaultStaticContentLoader", "setServeStaticContent", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "addDispatcherListener", "(DispatcherListener)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "cleanUpAfterInit", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "cleanUpRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "cleanup", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "getContainer", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "getInstance", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "init", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "isDevMode", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "isHandleException", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "removeDispatcherListener", "(DispatcherListener)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "serviceAction", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setDisableRequestAttributeValueStackLookup", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setDispatcherErrorHandler", "(DispatcherErrorHandler)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setHandleException", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setInstance", "(Dispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Dispatcher", "setMultipartSupportEnabled", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherErrorHandler", "handleError", "(HttpServletRequest,HttpServletResponse,int,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherErrorHandler", "init", "(ServletContext)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherListener", "dispatcherDestroyed", "(Dispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "DispatcherListener", "dispatcherInitialized", "(Dispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "ExecuteOperations", "executeAction", "(HttpServletRequest,HttpServletResponse,ActionMapping)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "ExecuteOperations", "executeStaticResourceRequest", "(HttpServletRequest,HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HostConfig", "getInitParameter", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HostConfig", "getInitParameterNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HostConfig", "getServletContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", "contains", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", "create", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "HttpParameters", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "InitOperations", "buildExcludedPatternsList", "(Dispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "InitOperations", "cleanup", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "InitOperations", "findDispatcherOnThread", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "InitOperations", "initStaticContentLoader", "(HostConfig,Dispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "LocalizedMessage", "getClazz", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter$Request", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", "getMultipleValues", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", "getObject", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", "getValue", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", "isDefined", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "Parameter", "isMultiple", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "assignDispatcherToThread", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "cleanupDispatcher", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "cleanupRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "cleanupWrappedRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "clearDevModeOverride", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "createActionContext", "(HttpServletRequest,HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "getDevModeOverride", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "isUrlExcluded", "(HttpServletRequest,List)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "overrideDevMode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "PrepareOperations", "trackRecursion", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "SessionMap", "invalidate", "()", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "StaticContentLoader", "canHandle", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "StaticContentLoader", "findStaticResource", "(String,HttpServletRequest,HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "StaticContentLoader", "setHostConfig", "(HostConfig)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "StrutsRequestWrapper", "StrutsRequestWrapper", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.dispatcher", "StrutsRequestWrapper", "StrutsRequestWrapper", "(HttpServletRequest,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "add", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "divide", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "isNumber", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "isNumberType", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "matches", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "mod", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "multiply", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELArithmetic", "subtract", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "checkType", "(Object,Class)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "coerceToBoolean", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "coerceToCharacter", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "coerceToNumber", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "coerceToNumber", "(Object,Class)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "compare", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "containsNulls", "(Object[])", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "equals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isBigDecimalOp", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isBigIntegerOp", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isDoubleOp", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isDoubleStringOp", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isLongOp", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "isStringFloat", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "throwUnhandled", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "toFloat", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ELSupport", "toNumber", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "ExpressionBuilder", "createNode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.lang", "FunctionMapperImpl$Function", "matches", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ArithmeticNode", "ArithmeticNode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstAnd", "AstAnd", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstBracketSuffix", "AstBracketSuffix", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstChoice", "AstChoice", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstCompositeExpression", "AstCompositeExpression", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstDeferredExpression", "AstDeferredExpression", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstDiv", "AstDiv", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstDotSuffix", "AstDotSuffix", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstDynamicExpression", "AstDynamicExpression", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstEmpty", "AstEmpty", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstEqual", "AstEqual", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFalse", "AstFalse", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFloatingPoint", "AstFloatingPoint", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFloatingPoint", "getFloatingPoint", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstFunction", "AstFunction", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstGreaterThan", "AstGreaterThan", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstGreaterThanEqual", "AstGreaterThanEqual", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstIdentifier", "AstIdentifier", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstInteger", "AstInteger", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstLessThan", "AstLessThan", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstLessThanEqual", "AstLessThanEqual", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstLiteralExpression", "AstLiteralExpression", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstMinus", "AstMinus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstMod", "AstMod", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstMult", "AstMult", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstNegative", "AstNegative", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstNot", "AstNot", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstNotEqual", "AstNotEqual", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstNull", "AstNull", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstOr", "AstOr", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstPlus", "AstPlus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstString", "AstString", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstTrue", "AstTrue", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "AstValue", "AstValue", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "BooleanNode", "BooleanNode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "And", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Boolean", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "BracketSuffix", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Choice", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Compare", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "DeferredExpression", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "DotSuffix", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "DynamicExpression", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Equality", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Expression", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "FloatingPoint", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Function", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Identifier", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Integer", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Literal", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "LiteralExpression", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Math", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Multiplication", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "NonLiteral", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Null", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Or", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "String", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Unary", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "Value", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "ValuePrefix", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "ValueSuffix", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "disable_tracing", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "enable_tracing", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParser", "parse", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "ELParserTokenManager", "SwitchTo", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", "clearNodeScope", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", "nodeArity", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", "nodeCreated", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", "openNodeScope", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "JJTELParserState", "reset", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "accept", "(NodeVisitor)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "getMethodInfo", "(EvaluationContext,Class[])", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "getType", "(EvaluationContext)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "getValue", "(EvaluationContext)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "invoke", "(EvaluationContext,Class[],Object[])", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "isReadOnly", "(EvaluationContext)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "jjtClose", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "jjtGetNumChildren", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "jjtOpen", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Node", "setValue", "(EvaluationContext,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "NodeVisitor", "visit", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "BeginToken", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "Done", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "adjustBeginLineColumn", "(int,int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "backup", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getBeginColumn", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getBeginLine", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getColumn", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getEndColumn", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getEndLine", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "getLine", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleCharStream", "readChar", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", "SimpleNode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "SimpleNode", "dump", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", "Token", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", "getValue", "()", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "Token", "newToken", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "TokenMgrError", "TokenMgrError", "(String,int)", "summary", "df-generated"] + - ["org.apache.struts2.el.parser", "TokenMgrError", "TokenMgrError", "(boolean,int,int,int,String,char,int)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ConcurrentCache", "ConcurrentCache", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String,Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String,Object,Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String,Object,Object,Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "get", "(String,Object,Object,Object,Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "MessageFactory", "getArray", "(String,Object[])", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ReflectionUtil", "forName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ReflectionUtil", "getMethod", "(Object,Object,Class[])", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ReflectionUtil", "getPropertyDescriptor", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ReflectionUtil", "toTypeArray", "(String[])", "summary", "df-generated"] + - ["org.apache.struts2.el.util", "ReflectionUtil", "toTypeNameArray", "(Class[])", "summary", "df-generated"] + - ["org.apache.struts2.el", "ValueExpressionLiteral", "equals", "(ValueExpressionLiteral)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspInterceptor", "setEnforcingMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspSettings", "addCspHeaders", "(HttpServletRequest,HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspSettings", "addCspHeaders", "(HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.csp", "CspSettings", "setEnforcingMode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "DebuggingInterceptor", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "DebuggingInterceptor", "setEnableXmlWithConsole", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "PrettyPrintWriter", "(Writer)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "close", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "endNode", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "flush", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "isEscape", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.debugging", "PrettyPrintWriter", "setValue", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "BackgroundProcess", "isDone", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "ExecutorProvider", "execute", "(Runnable)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "ExecutorProvider", "isShutdown", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.exec", "ExecutorProvider", "shutdown", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor.httpmethod", "HttpMethod", "parse", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "CoepInterceptor", "setEnforcingMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "CookieInterceptor", "setAcceptCookieNames", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "CoopInterceptor", "isExempted", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "DateTextFieldInterceptor$DateWord", "get", "(Character)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "DateTextFieldInterceptor$DateWord", "getAll", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "DateTextFieldInterceptor$DateWord", "getDateType", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "DateTextFieldInterceptor$DateWord", "getDescription", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "DateTextFieldInterceptor$DateWord", "getLength", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", "setDelay", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", "setDelaySleepInterval", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", "setExecuteAfterValidationPass", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ExecuteAndWaitInterceptor", "setThreadPriority", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "FileUploadInterceptor", "setMaximumSize", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", "setLocaleStorage", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "I18nInterceptor", "setSupportedLocale", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", "getAllowRequestParameterSwitch", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "MessageStoreInterceptor", "setAllowRequestParameterSwitch", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", "getRemoteUser", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", "getUserPrincipal", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", "isRequestSecure", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "PrincipalProxy", "isUserInRole", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ResourceIsolationPolicy", "isRequestAllowed", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", "isReset", "()", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", "setAutoCreateSession", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.interceptor", "ScopeInterceptor", "setReset", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPlugin", "doTag", "(TagPluginContext)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "dontUseTagPlugin", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "generateAttribute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "generateBody", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "generateDeclaration", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "generateImport", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "generateJavaSource", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "getConstantAttribute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "getParentContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "getTemporaryVariableName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "isAttributeSpecified", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "isConstantAttribute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler.tagplugin", "TagPluginContext", "isScriptless", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", "checkVariable", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "BeanRepository", "getBeanType", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "compile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "compile", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "compile", "(boolean,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "isOutDated", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "isOutDated", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "removeGeneratedClassFiles", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Compiler", "removeGeneratedFiles", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELFunctionMapper", "map", "(Compiler,Nodes)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", "containsEL", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Nodes", "isEmpty", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Root", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Visitor", "visit", "(ELText)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Visitor", "visit", "(Function)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode$Visitor", "visit", "(Text)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELNode", "accept", "(Visitor)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELParser", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ELParser", "parse", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "ErrorDispatcher", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "javacError", "(JavacErrorDetail[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "javacError", "(String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Mark,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Mark,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Mark,String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Mark,String,String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Node,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Node,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Node,String,String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Node,String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(Node,String,String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(String,String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorDispatcher", "jspError", "(String,String,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorHandler", "javacError", "(JavacErrorDetail[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorHandler", "javacError", "(String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorHandler", "jspError", "(String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ErrorHandler", "jspError", "(String,int,int,String,Exception)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", "getJavaLineNumber", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JavacErrorDetail", "getJspBeginLineNumber", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspConfig", "isJspPage", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "checkCompile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "getJspCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "getJspReloadCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "incrementJspReloadCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "removeWrapper", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspRuntimeContext", "setJspReloadCount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "booleanValue", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "checkAttributes", "(String,Node,ValidAttribute[],ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "checkScope", "(String,Node,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "escapeQueryString", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "escapeXml", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "getCanonicalName", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "getExprInXml", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "getInputStream", "(String,JarFile,JspCompilationContext,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "getTagHandlerClassName", "(String,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "getTagHandlerClassName", "(String,String,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "isExpression", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "isJavaKeyword", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "makeJavaIdentifier", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "makeJavaPackage", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "mangleChar", "(char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "nextTemporaryVariableName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "removeQuotes", "(char[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "resetTemporaryVariableName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "toClass", "(String,ClassLoader)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "JspUtil", "validateExpressions", "(Mark,String,Class,FunctionMapper,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "hasIncludeAction", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "hasParamAction", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "hasScriptingVars", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "hasSetProperty", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "hasUseBean", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "isScriptless", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setHasIncludeAction", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setHasParamAction", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setHasScriptingVars", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setHasSetProperty", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setHasUseBean", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ChildInfo", "setScriptless", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "checkIfAttributeIsJspFragment", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "getCustomNestingLevel", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "getNumCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "getTagAttributeInfo", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "getTagHandlerClass", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "getTagVariableInfos", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "hasEmptyBody", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsBodyTag", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsDynamicAttributes", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsIterationTag", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsJspIdConsumer", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsSimpleTag", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "implementsTryCatchFinally", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "isTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "setNumCount", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "setTagHandlerClass", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "setUseTagPlugin", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$CustomTag", "useTagPlugin", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$ELExpression", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "getExpectedTypeName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "getParameterTypeNames", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isDeferredInput", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isDeferredMethodInput", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isDynamic", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isELInterpreterInput", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isExpression", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isLiteral", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "isNamedAttribute", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$JspAttribute", "validateEL", "(ExpressionFactory,ELContext)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$NamedAttribute", "isTrim", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", "isGeneratedInBuffer", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", "remove", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", "setGeneratedInBuffer", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Nodes", "size", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "isBomPresent", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "isDefaultPageEncoding", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "isEncodingSpecifiedInProlog", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "isXmlSyntax", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "nextTemporaryVariableName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "setIsBomPresent", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "setIsDefaultPageEncoding", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Root", "setIsEncodingSpecifiedInProlog", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", "addSmap", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", "isAllSpace", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", "ltrim", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$TemplateText", "rtrim", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "doVisit", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(AttributeDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(Comment)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(CustomTag)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(Declaration)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(DoBodyAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(ELExpression)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(Expression)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(ForwardAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(GetProperty)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(IncludeAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(IncludeDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(InvokeAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(JspBody)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(JspElement)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(JspOutput)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(JspRoot)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(JspText)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(NamedAttribute)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(PageDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(ParamAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(ParamsAction)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(PlugIn)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(Root)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(Scriptlet)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(SetProperty)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(TagDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(TaglibDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(TemplateText)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(UninterpretedTag)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(UseBean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node$Visitor", "visit", "(VariableDirective)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "Node", "getText", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "close", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "getJavaLine", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "popIndent", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "print", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "print", "(char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "print", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "printComment", "(Mark,Mark,char[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "printMultiLn", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "printil", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "printin", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "printin", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "println", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "println", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "ServletWriter", "pushIndent", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapGenerator", "setDoEmbedded", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "getString", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "setInputLineCount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "setInputStartLine", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "setLineFileID", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "setOutputLineIncrement", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "setOutputStartLine", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum$LineInfo", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", "addLineData", "(int,String,int,int,int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapStratum", "optimizeLineSection", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapUtil", "evaluateNodes", "(Nodes,SmapStratum,HashMap,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "SmapUtil", "installSmap", "(String[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TextOptimizer", "concatenate", "(Compiler,Nodes)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", "getAbsolutePathsOfLocations", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", "setNoTldJars", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.compiler", "TldLocationsCache", "uriType", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspELException", "JspELException", "(String,ELException)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspMethodNotFoundException", "JspMethodNotFoundException", "(String,MethodNotFoundException)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspPropertyNotFoundException", "JspPropertyNotFoundException", "(String,PropertyNotFoundException)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.el", "JspPropertyNotWritableException", "JspPropertyNotWritableException", "(String,PropertyNotWritableException)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "BodyContentImpl", "BodyContentImpl", "(JspWriter)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "HttpJspBase", "_jspInit", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "InstanceHelper", "getClassLoaderInstanceManager", "(ClassLoader)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "InstanceHelper", "postConstruct", "(InstanceManager,Object)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "InstanceHelper", "preDestroy", "(InstanceManager,Object)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", "syncBeforeInvoke", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", "syncBeginTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspContextWrapper", "syncEndTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "URLEncode", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerce", "(String,Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToBoolean", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToByte", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToChar", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToDouble", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToFloat", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToInt", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToLong", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "coerceToShort", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "createTypedArray", "(String,Object,Method,String[],Class,Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "escapeQueryString", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "getReadMethod", "(Class,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "getValueFromBeanInfoPropertyEditor", "(Class,String,String,Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "getValueFromPropertyEditorManager", "(Class,String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "getWriteMethod", "(Class,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleGetProperty", "(Object,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,byte)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,double)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,float)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,long)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetProperty", "(Object,String,short)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "handleSetPropertyExpression", "(Object,String,String,PageContext,ProtectedFunctionMapper)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "introspect", "(Object,ServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "introspecthelper", "(Object,String,String,ServletRequest,String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(byte)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(double)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(float)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "JspRuntimeLibrary", "toString", "(short)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "PageContextImpl", "proprietaryEvaluate", "(String,Class,PageContext,ProtectedFunctionMapper,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "ProtectedFunctionMapper", "getInstance", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "ProtectedFunctionMapper", "getMapForFunction", "(String,Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", "TagHandlerPool", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", "get", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", "release", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.runtime", "TagHandlerPool", "reuse", "(Tag)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.security", "SecurityClassLoad", "securityClassLoad", "(ClassLoader)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.security", "SecurityUtil", "filter", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.security", "SecurityUtil", "isPackageProtectionEnabled", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServlet", "getJspCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServlet", "getJspReloadCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServlet", "setJspReloadCount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "decTripCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "getDependants", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "getLastModificationTest", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "incTripCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "isTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "loadTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "loadTagFilePrototype", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "service", "(HttpServletRequest,HttpServletResponse,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "setLastModificationTest", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "setReload", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.servlet", "JspServletWrapper", "setServletClassLastModifiedTime", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util$ImportResponseWrapper", "ImportResponseWrapper", "(HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", "getScope", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.tagplugins.jstl", "Util", "isAbsoluteUrl", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "getIANA2JavaMapping", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "getJava2IANAMapping", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "putIANA2JavaMapping", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "putJava2IANAMapping", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "removeIANA2JavaMapping", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "EncodingMap", "removeJava2IANAMapping", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "ParserUtils", "parseXMLDocument", "(String,InputSource)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "ParserUtils", "parseXMLDocument", "(String,InputStream)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", "SymbolTable", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", "containsSymbol", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", "containsSymbol", "(char[],int,int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", "hash", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "SymbolTable", "hash", "(char[],int,int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", "removeAttribute", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "TreeNode", "removeNode", "(TreeNode)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "highSurrogate", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isContent", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isHighSurrogate", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isInvalid", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isLowSurrogate", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isMarkup", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isNCName", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isNCNameStart", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isName", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isNameStart", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isPubid", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isSpace", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isSupplemental", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValid", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValidIANAEncoding", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValidJavaEncoding", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValidNCName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValidName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "isValidNmtoken", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "lowSurrogate", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLChar", "supplemental", "(char,char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "getEncoding", "(String,JarFile,JspCompilationContext,ErrorDispatcher)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "isExternal", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "peekChar", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "scanChar", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "skipChar", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "skipSpaces", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLEncodingDetector", "skipString", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", "clear", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", "equals", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLString", "equals", "(char[],int,int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", "XMLStringBuffer", "(char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", "XMLStringBuffer", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper.xmlparser", "XMLStringBuffer", "append", "(char)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "EmbeddedServletOptions", "setErrorOnUseBeanInvalidClassAttribute", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JasperException", "JasperException", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JasperException", "JasperException", "(String,Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JasperException", "JasperException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "execute", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getFailOnError", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getJspCompilerPath", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getJspCompilerPlugin", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getProtectionDomain", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getTagPoolSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "getTldAbsolutePaths", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setAddWebXmlMappings", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setCaching", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setClassDebugInfo", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setCompile", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setErrorOnUseBeanInvalidClassAttribute", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setFailOnError", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setGenStringAsCharArray", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setListErrors", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setPoolingEnabled", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setSmapDumped", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setSmapSuppressed", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setTrimSpaces", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setValidateXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setVerbose", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspC", "setXpoweredBy", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "checkOutputDir", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "compile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "getResource", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "getResourceAsStream", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "getResourcePaths", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "getTldLocation", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "incrementRemoved", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "isErrorPage", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "isPrototypeMode", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "isRemoved", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "isTagFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "keepGenerated", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "load", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "setErrorPage", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "JspCompilationContext", "setPrototypeMode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "genStringAsCharArray", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getCheckInterval", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getClassDebugInfo", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getCompilerClassName", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getDevelopment", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getDisplaySourceFragment", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getErrorOnUseBeanInvalidClassAttribute", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getFork", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getKeepGenerated", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getMappedFile", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getModificationTestInterval", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "getTrimSpaces", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "isCaching", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "isPoolingEnabled", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "isSmapDumped", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "isSmapSuppressed", "()", "summary", "df-generated"] + - ["org.apache.struts2.jasper", "Options", "isXpoweredBy", "()", "summary", "df-generated"] + - ["org.apache.struts2.json.bridge", "FieldBridge", "objectToString", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", "getJsonResultExcludeProxyProperties", "()", "summary", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", "setJsonResultExcludeProxyProperties", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json.config.entities", "JSONConstantConfig", "setJsonWriter", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", "getCode", "()", "summary", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCError", "setCode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCErrorCode", "code", "()", "summary", "df-generated"] + - ["org.apache.struts2.json.rpc", "RPCErrorCode", "message", "()", "summary", "df-generated"] + - ["org.apache.struts2.json.smd", "SMDGenerator", "generate", "(ActionInvocation)", "summary", "df-generated"] + - ["org.apache.struts2.json", "DefaultJSONWriter", "setExcludeProxyProperties", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", "isDefaultBlock", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONCleaner", "setDefaultBlock", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONException", "JSONException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "getDebug", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "isEnableGZIP", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "isEnableSMD", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "isExcludeNullProperties", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "isNoCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setDebug", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setEnableGZIP", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setEnableSMD", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setExcludeNullProperties", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setIgnoreHierarchy", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setIgnoreSMDMethodInterfaces", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setNoCache", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setPrefix", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONInterceptor", "setWrapWithComments", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONPopulator", "populateObject", "(Object,Map)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isEnableGZIP", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isEnableSMD", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isEnumAsBean", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isExcludeNullProperties", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isIgnoreHierarchy", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isNoCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "isWrapWithComments", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setDevMode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setEnableGZIP", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setEnableSMD", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setEnumAsBean", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setErrorCode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setExcludeNullProperties", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setIgnoreHierarchy", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setIgnoreInterfaces", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setNoCache", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setPrefix", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setStatusCode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONResult", "setWrapWithComments", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil$ClassVisitor", "visit", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "deserialize", "(Reader)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "deserialize", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "isGzipInRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "listSMDMethods", "(Class,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "setContainer", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "visitInterfaces", "(Class,ClassVisitor)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONUtil", "writeJSONToResponse", "(SerializationParams)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", "isJsonEnabled", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", "isSetEncoding", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", "isValidateOnly", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONValidationInterceptor", "setValidationFailedStatus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", "setCacheBeanInfo", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", "setDateFormatter", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", "setEnumAsBean", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", "setExcludeProxyProperties", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "JSONWriter", "setIgnoreHierarchy", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "getErrorCode", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "getStatusCode", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "isGzip", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "isNoCache", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "isPrefix", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "isSmd", "()", "summary", "df-generated"] + - ["org.apache.struts2.json", "SerializationParams", "isWrapWithComments", "()", "summary", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", "assertEquals", "(URL,String)", "summary", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", "compare", "(URL,String)", "summary", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", "normalize", "(URL)", "summary", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", "readContent", "(URL)", "summary", "df-generated"] + - ["org.apache.struts2.junit.util", "TestUtils", "readContent", "(URL,Charset)", "summary", "df-generated"] + - ["org.apache.struts2.junit", "StrutsJUnit4TestCase", "finishExecution", "()", "summary", "df-generated"] + - ["org.apache.struts2.junit", "XWorkJUnit4TestCase", "setUp", "()", "summary", "df-generated"] + - ["org.apache.struts2.junit", "XWorkJUnit4TestCase", "tearDown", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockActionRequest", "MockActionRequest", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockBaseURL", "isSecure", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockEvent", "MockEvent", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockMimeResponse", "setCommitted", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", "close", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", "isActive", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", "setRequestedSessionIdValid", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", "setSecure", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletRequest", "setServerPort", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletSession", "access", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletSession", "clearAttributes", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletSession", "isInvalid", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletSession", "setNew", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockPortletURL", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceResponse", "getContentLength", "()", "summary", "df-generated"] + - ["org.apache.struts2.mock.web.portlet", "MockResourceURL", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "displayProperty", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "getBundle", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "getBundleState", "(Bundle)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "getBundles", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "getHeaderKeys", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "getPackages", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "index", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "isAllowedAction", "(Bundle,String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "isStrutsEnabled", "(Bundle)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "start", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "stop", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "update", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "BundlesAction", "view", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.admin.actions", "ShellAction", "executeCommand", "(String,PrintStream,PrintStream)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", "getActiveBundles", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", "getBundleContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", "getBundles", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi.host", "OsgiHost", "init", "(ServletContext)", "summary", "df-generated"] + - ["org.apache.struts2.osgi.loaders", "VelocityBundleResourceLoader", "getResourceStream", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "getAllServiceReferences", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "getService", "(ServiceReference)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "getServiceReference", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "getServiceReferences", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "loadClass", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "loadResourceAsStream", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "loadResourceFromAllBundles", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "BundleAccessor", "loadResourceFromAllBundlesAsStream", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DefaultBundleAccessor", "getInstance", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DefaultBundleAccessor", "loadResource", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DefaultBundleAccessor", "loadResource", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DefaultBundleAccessor", "loadResources", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DefaultBundleAccessor", "loadResources", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "DelegatingObjectFactory", "setDelegateObjectFactory", "(Container,String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiConfigurationProvider", "setVelocityManager", "(VelocityManager)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiHostFactory", "createOsgiHost", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiUtil", "containsBean", "(Object,String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiUtil", "generateJava_SE_SystemPackageVersionString", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "OsgiUtil", "getBean", "(Object,String)", "summary", "df-generated"] + - ["org.apache.struts2.osgi", "PackageLoader", "loadPackages", "(Bundle,BundleContext,ObjectFactory,FileManagerFactory,Map)", "summary", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "DefaultOValValidationManager", "setReloadingConfigs", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationInterceptor", "setAlwaysInvokeValidate", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationInterceptor", "setProgrammatic", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.oval.interceptor", "OValValidationInterceptor", "setValidateJPAAnnotations", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusFilter", "isLoaded", "()", "summary", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusLifecycleListener", "isLoaded", "()", "summary", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusThreadLocal", "getPlexusContainer", "()", "summary", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusThreadLocal", "setPlexusContainer", "(PlexusContainer)", "summary", "df-generated"] + - ["org.apache.struts2.plexus", "PlexusUtils", "configure", "(PlexusContainer,String)", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getActionRequest", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getActionResponse", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getDefaultActionForMode", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getModeActionMap", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getModeNamespaceMap", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getPhase", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getPortletConfig", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getPortletContext", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getPortletNamespace", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getRenderRequest", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getRenderResponse", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getRequest", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getResourceResponse", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "getResponse", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "isJSR268Supported", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.context", "PortletActionContext", "isPortletRequest", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet.dispatcher", "Jsr168Dispatcher", "serviceAction", "(PortletRequest,PortletResponse,Map,Map,Map,Map,String,PortletPhase)", "summary", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletResult", "setUseDispatcherServlet", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.portlet.result", "PortletVelocityResult", "executeRenderResult", "(String,ActionInvocation)", "summary", "df-generated"] + - ["org.apache.struts2.portlet", "PortletPhase", "isAction", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet", "PortletPhase", "isEvent", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet", "PortletPhase", "isRender", "()", "summary", "df-generated"] + - ["org.apache.struts2.portlet", "PortletPhase", "isResource", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", "getRestContentRestrictToGet", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", "getRestLogger", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", "setRestContentRestrictToGet", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.rest.config.entities", "RestConstantConfig", "setRestLogger", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "fromObject", "(ActionInvocation,Object,String,Writer)", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "fromObject", "(Object,String,Writer)", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "getContentType", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "getExtension", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "toObject", "(ActionInvocation,Reader,Object)", "summary", "df-generated"] + - ["org.apache.struts2.rest.handler", "ContentTypeHandler", "toObject", "(Reader,Object)", "summary", "df-generated"] + - ["org.apache.struts2.rest", "DefaultContentTypeHandlerManager", "setContainer", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.rest", "HttpHeaders", "getStatus", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest", "HttpHeaders", "setStatus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionInvocation", "setLogger", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionInvocation", "setRestrictToGet", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionSupport", "createContinue", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionSupport", "index", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionSupport", "options", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestActionSupport", "updateContinue", "()", "summary", "df-generated"] + - ["org.apache.struts2.rest", "RestWorkflowInterceptor", "setValidationFailureStatusCode", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AbstractAdapterNode", "getElementsByTagName", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AbstractAdapterNode", "getElementsByTagNameNS", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", "getAdapterForValue", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterFactory", "registerAdapterType", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", "getChildAfter", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "AdapterNode", "getChildBefore", "(Node)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", "getParseStringAsXML", "()", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "StringAdapter", "setParseStringAsXML", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", "getStatus", "()", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", "setNoCache", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", "setParse", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result.xslt", "XSLTResult", "setStatus", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", "HttpHeaderResult", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", "getStatus", "()", "summary", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", "setParse", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "HttpHeaderResult", "setStatus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.result", "PlainResult", "ignoreCommitted", "()", "summary", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", "setCache", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "PostbackResult", "setPrependServletContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", "setPrependServletContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", "setStatusCode", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.result", "ServletRedirectResult", "setSuppressEmptyParameters", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", "getAllowCaching", "()", "summary", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", "getBufferSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", "setAllowCaching", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "StreamResult", "setBufferSize", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", "doExecute", "(String,ActionInvocation)", "summary", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", "setEncode", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.result", "StrutsResultSupport", "setParse", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "AbstractCRUDAction", "delete", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "AbstractCRUDAction", "list", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", "getAvailableLevels", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", "getAvailablePositions", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", "getEmpId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", "save", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "EmployeeAction", "setEmpId", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getBands", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getBook", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getBooks", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getCurrentDate", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getMovies", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "ExampleAction", "getName", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "JSPEvalAction", "cleanUp", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.action", "SkillAction", "save", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", "getById", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", "getId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", "isToggle", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", "setId", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "Category", "toggle", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax.tree", "GetCategory", "setCatId", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AjaxTestAction", "getCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "AjaxTestAction", "getServerTime", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", "getTodayDate", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", "getTodayTime", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", "panel1", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", "panel2", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example4ShowPanelAction", "panel3", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example5Action", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.ajax", "Example5Action", "setAge", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.application", "MemoryStorage", "reset", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", "create", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", "delete", "(Class,Serializable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", "delete", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.application", "Storage", "merge", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.async", "ChatRoomAction", "receiveNewMessages", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.async", "ChatRoomAction", "sendMessage", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.async", "ChatRoomAction", "setLastIndex", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", "enterRoom", "(User,String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", "exitRoom", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", "logout", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "ChatService", "sendMessageToRoom", "(String,User,String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "CrudRoomAction", "create", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", "hasMember", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.chat", "Room", "memberExit", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "AddressAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "OperationsEnumAction", "getAvailableOperations", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "OperationsEnumAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Person", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "Person", "setAge", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.conversion", "PersonAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", "create", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", "delete", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", "delete", "(Serializable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", "getFeaturedClass", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.dao", "Dao", "merge", "(IdEntity)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.exception", "CreateException", "CreateException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DeleteException", "DeleteException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.exception", "DuplicateKeyException", "DuplicateKeyException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.exception", "StorageException", "StorageException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.exception", "UpdateException", "UpdateException", "(Throwable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.filedownload", "FileDownloadAction", "getInputStream", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", "getUploadSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "FileUploadAction", "upload", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingArrayAction", "upload", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.fileupload", "MultipleFileUploadUsingListAction", "upload", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "CustomFreemarkerManagerUtil", "getTimeNow", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.freemarker", "CustomFreemarkerManagerUtil", "getTodayDate", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "GuessCharacterAction", "getCharacter", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "GuessCharacterAction", "setCharacter", "(Character)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", "characterGuessedBefore", "(Character)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", "gameEnded", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", "guess", "(Character)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", "guessLeft", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Hangman", "isWin", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "HangmanException", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", "containCharacter", "(Character)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.hangman", "Vocab", "containsAllCharacter", "(List)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "getEmpId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "getSalary", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "isMarried", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "setEmpId", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "setMarried", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Employee", "setSalary", "(Float)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "IdEntity", "getId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "IdEntity", "setId", "(Serializable)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", "getDescription", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", "getName", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.model", "Skill", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", "isBustedBefore", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", "setAge", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.modelDriven", "Gangster", "setBustedBefore", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "EditPersonAction", "save", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "ListPeopleAction", "getPeopleCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", "getId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "Person", "setId", "(Long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "PersonManager", "createPerson", "(Person)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "PersonManager", "getPeople", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.person", "PersonManager", "updatePerson", "(Person)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", "getConfigLine", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", "getPadding", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.source", "ViewSourceAction", "setPadding", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.actionPrefix", "SubmitAction", "alternateMethod", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.actiontag", "ActionTagDemo", "doInclude", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.actiontag", "ActionTagDemo", "show", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "AppendIteratorTagDemo", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", "getCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", "setCount", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "IteratorGeneratorTagDemo", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "MergeIteratorTagDemo", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", "getCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", "getStart", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", "setCount", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", "setStart", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.tag.nonui.iteratortag", "SubsetIteratorTagDemo", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.token", "TokenAction", "getAmount", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.token", "TokenAction", "setAmount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AbstractValidationActionSupport", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", "getIntegerValidatorField", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitAction", "setIntegerValidatorField", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "AjaxFormSubmitSuccessAction", "execute", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", "beanValidation", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", "getIntegerValidatorField", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "BeanValidationExampleAction", "setIntegerValidatorField", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", "getIntegerValidatorField", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "FieldValidatorsExampleAction", "setIntegerValidatorField", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "QuizAction", "setAge", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", "applicationOk", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", "cancelApplication", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", "setAge", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "SubmitApplication", "submitApplication", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", "getAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.validation", "User", "setAge", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.wait", "LongProcessAction", "getTime", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase.wait", "LongProcessAction", "setTime", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase.xslt", "JVMAction", "execute", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", "browse", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "DateAction", "getDate", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "DynamicTreeSelectAction", "getNodeId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "DynamicTreeSelectAction", "setNodeId", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getAvailableCities", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavoriteSports", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavouriteCars", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavouriteCartoonCharacters", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavouriteCities", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavouriteCountries", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultFavouriteMotorcycles", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultNonFavoriteSports", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultNotFavouriteCars", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultNotFavouriteCartoonCharacters", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultNotFavouriteCountries", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "getDefaultNotFavouriteMotorcycles", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfOptiontransferselectAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "LotsOfRichtexteditorAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getAvailableCities", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getDefaultFavouriteCars", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getDefaultFavouriteCartoonCharacters", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getDefaultFavouriteCities", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getDefaultFavouriteCountries", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "getDefaultFavouriteNumbers", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "MoreSelectsAction", "submit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "ShowAjaxDynamicTreeAction", "getCategory", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "ShowAjaxDynamicTreeAction", "getNodeId", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "ShowAjaxDynamicTreeAction", "setNodeId", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "ShowDynamicTreeAction", "getTreeRootNode", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", "doSubmit", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", "isLegalAge", "()", "summary", "df-generated"] + - ["org.apache.struts2.showcase", "UITagExample", "setLegalAge", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.sitemesh", "FreemarkerPageFilter", "setFreemarkerManager", "(FreemarkerManager)", "summary", "df-generated"] + - ["org.apache.struts2.sitemesh", "OldDecorator2NewStrutsFreemarkerDecorator", "setFreemarkerManager", "(FreemarkerManager)", "summary", "df-generated"] + - ["org.apache.struts2.sitemesh", "OldDecorator2NewStrutsVelocityDecorator", "setVelocityManager", "(VelocityManager)", "summary", "df-generated"] + - ["org.apache.struts2.sitemesh", "StrutsSiteMeshFactory", "StrutsSiteMeshFactory", "(Config)", "summary", "df-generated"] + - ["org.apache.struts2.sitemesh", "VelocityPageFilter", "setVelocityManager", "(VelocityManager)", "summary", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", "getClassReloadingReloadConfig", "()", "summary", "df-generated"] + - ["org.apache.struts2.spring.config.entities", "SpringConstantConfig", "setClassReloadingReloadConfig", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.spring", "ClassReloadingXMLWebApplicationContext", "setupReloading", "(String[],String,ServletContext,boolean)", "summary", "df-generated"] + - ["org.apache.struts2.spring", "StrutsSpringObjectFactory", "StrutsSpringObjectFactory", "(String,String,String,String,ServletContext,String,Container)", "summary", "df-generated"] + - ["org.apache.struts2.tiles", "StrutsTilesAnnotationProcessor", "buildTilesDefinition", "(String,TilesDefinition)", "summary", "df-generated"] + - ["org.apache.struts2.tiles", "StrutsTilesAnnotationProcessor", "findAnnotation", "(Object,String)", "summary", "df-generated"] + - ["org.apache.struts2.tiles", "StrutsWildcardServletApplicationContext", "StrutsWildcardServletApplicationContext", "(ServletContext)", "summary", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", "contains", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser$Result", "isEmpty", "()", "summary", "df-generated"] + - ["org.apache.struts2.url", "QueryStringParser", "empty", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "AttributeMap", "toString", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "ComponentUtils", "containsExpression", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "ComponentUtils", "isExpression", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "ContainUtil", "contains", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getCurrent", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getFirst", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getInterval", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getLast", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getNext", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "getPrevious", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "isWrap", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setAdd", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setCurrent", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setFirst", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setInterval", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setLast", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Counter", "setWrap", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", "FastByteArrayOutputStream", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", "getSize", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "FastByteArrayOutputStream", "writeTo", "(RandomAccessFile)", "summary", "df-generated"] + - ["org.apache.struts2.util", "InvocationSessionStore", "loadInvocation", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "InvocationSessionStore", "storeInvocation", "(String,String,ActionInvocation)", "summary", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", "getHasNext", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "IteratorGenerator", "setCount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "ListEntry", "getIsSelected", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "MakeIterator", "isIterable", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "ObjectFactoryDestroyable", "destroy", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "PrefixTrie", "get", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "PrefixTrie", "put", "(String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsTestCaseHelper", "tearDown", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsTypeConverter", "convertFromString", "(Map,String[],Class)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsTypeConverter", "convertToString", "(Map,Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "include", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "isTrue", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toInt", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toLong", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toLong", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toString", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toString", "(long)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "toStringSafe", "(Object)", "summary", "df-generated"] + - ["org.apache.struts2.util", "StrutsUtil", "translateVariables", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "SubsetIteratorFilter", "setCount", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "SubsetIteratorFilter", "setStart", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", "TabbedPane", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", "getSelectedIndex", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TabbedPane", "setSelectedIndex", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.util", "Timer", "getTime", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "Timer", "getTotal", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "generateGUID", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "getToken", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "getToken", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "getTokenName", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "setSessionToken", "(String,String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "setToken", "()", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "setToken", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.util", "TokenHelper", "validToken", "()", "summary", "df-generated"] + - ["org.apache.struts2.validators", "DWRValidator", "doPost", "(String,String,Map)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "getDebug", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "getNoCharsetInContentType", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "getNocache", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "setCacheBeanWrapper", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "setFileManagerFactory", "(FileManagerFactory)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "setMruMaxStrongSize", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerManager", "setWrapperAltMap", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", "isWriteIfCompleted", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "FreemarkerResult", "setWriteIfCompleted", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.freemarker", "StrutsBeanWrapper", "StrutsBeanWrapper", "(boolean,Version)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", "getGxpClass", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", "getGxpClassForPath", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", "getGxpClosure", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxp", "write", "(Appendable,GxpContext)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "AbstractGxpResult", "setUseInstances", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "Gxp", "getInstance", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "Gxp", "getInstance", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "GxpInstance", "getInstance", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "GxpInstance", "getInstance", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "GxpResult", "setOutputXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "Param", "getType", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.gxp", "Param", "isOptional", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "JasperReportsResult", "setWrapField", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jasperreports", "ValueStackShadowMap", "containsKey", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "DefaultTagHandlerFactory", "DefaultTagHandlerFactory", "(Class)", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "JavaTemplateEngine", "setThemeClasses", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "TagGenerator", "generate", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", "characters", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "TagHandler", "setNext", "(TagHandler)", "summary", "df-generated"] + - ["org.apache.struts2.views.java", "Theme", "renderTag", "(String,TemplateRenderingContext)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ActionErrorTag", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "ActionMessageTag", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FieldErrorTag", "setEscape", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "FormTag", "setIncludeContext", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "OgnlTool", "findValue", "(String,Object)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp.ui", "SubmitTag", "setEscapeHtmlBody", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", "getFlush", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", "setExecuteResult", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", "setFlush", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", "setIgnoreContextParams", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ActionTag", "setRethrowException", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ComponentTagSupport", "getBean", "(ValueStack,HttpServletRequest,HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "DateTag", "setNice", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus$StatusState", "next", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus$StatusState", "setLast", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "getCount", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "getIndex", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "isEven", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "isFirst", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "isLast", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "isOdd", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "IteratorStatus", "modulus", "(int)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setGroupingUsed", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setMaximumFractionDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setMaximumIntegerDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setMinimumFractionDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setMinimumIntegerDigits", "(Integer)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "NumberTag", "setParseIntegerOnly", "(Boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "ParamTag", "setSuppressEmptyParameters", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", "setEscapeCsv", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", "setEscapeHtml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", "setEscapeJavaScript", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "PropertyTag", "setEscapeXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "SetTag", "setTrimBody", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "StrutsBodyTagSupport", "setPerformClearTagStateForTagPoolingServers", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "TextTag", "setEscapeCsv", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "TextTag", "setEscapeHtml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "TextTag", "setEscapeJavaScript", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.jsp", "TextTag", "setEscapeXml", "(boolean)", "summary", "df-generated"] + - ["org.apache.struts2.views.util", "DefaultUrlHelper", "setHttpPort", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.util", "DefaultUrlHelper", "setHttpsPort", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity.components", "AbstractDirective", "getBeanName", "()", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity", "StrutsResourceLoader", "getResourceStream", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", "init", "(ServletContext)", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityManager", "setContainer", "(Container)", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityStrutsUtil", "evaluate", "(String)", "summary", "df-generated"] + - ["org.apache.struts2.views.velocity", "VelocityTagLibrary", "getVelocityDirectiveClasses", "()", "summary", "df-generated"] + - ["org.apache.struts2.views", "TagLibraryDirectiveProvider", "getDirectiveClasses", "()", "summary", "df-generated"] + - ["org.apache.struts2", "JSPLoader", "load", "(String)", "summary", "df-generated"] + - ["org.apache.struts2", "JSPRuntime", "clearCache", "()", "summary", "df-generated"] + - ["org.apache.struts2", "JSPRuntime", "handle", "(String)", "summary", "df-generated"] + - ["org.apache.struts2", "JSPRuntime", "handle", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.struts2", "RequestUtils", "parseIfModifiedSince", "(String)", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getActionContext", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getActionContext", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getActionMapping", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getContext", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getPageContext", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getRequest", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getResponse", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "getServletContext", "()", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "setRequest", "(HttpServletRequest)", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "setResponse", "(HttpServletResponse)", "summary", "df-generated"] + - ["org.apache.struts2", "ServletActionContext", "setServletContext", "(ServletContext)", "summary", "df-generated"] + - ["org.apache.struts2", "ServletCache", "clear", "()", "summary", "df-generated"] + - ["org.w3c.dom", "Document", "getElementsByTagName", "(String)", "summary", "df-generated"] + - ["org.w3c.dom", "Document", "getElementsByTagNameNS", "(String,String)", "summary", "df-generated"] diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml index 98c51a7bad5..e4d543aa06d 100644 --- a/java/ql/lib/ext/java.io.model.yml +++ b/java/ql/lib/ext/java.io.model.yml @@ -84,6 +84,7 @@ extensions: - ["java.io", "File", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.io", "File", True, "toURI", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.io", "FilterOutputStream", True, "FilterOutputStream", "(OutputStream)", "", "Argument[0]", "Argument[this]", "taint", "manual"] + - ["java.io", "InputStream", True, "read", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.io", "InputStream", True, "read", "(byte[])", "", "Argument[this]", "Argument[0]", "taint", "manual"] - ["java.io", "InputStream", True, "read", "(byte[],int,int)", "", "Argument[this]", "Argument[0]", "taint", "manual"] - ["java.io", "InputStream", True, "readAllBytes", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml index 7b151739a07..53cd7eee4ed 100644 --- a/java/ql/lib/ext/java.nio.file.model.yml +++ b/java/ql/lib/ext/java.nio.file.model.yml @@ -17,11 +17,11 @@ extensions: - ["java.nio.file", "Files", False, "createTempFile", "(Path,String,String,FileAttribute[])", "", "Argument[0]", "path-injection", "manual"] - ["java.nio.file", "Files", False, "delete", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "deleteIfExists", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - - ["java.nio.file", "Files", False, "deleteIfExists", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "getFileStore", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] # the FileStore class is unlikely to be used for later sanitization - ["java.nio.file", "Files", False, "lines", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "lines", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "move", "", "", "Argument[1]", "path-injection", "manual"] + - ["java.nio.file", "Files", False, "move", "(Path,Path,CopyOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "newBufferedReader", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "newBufferedReader", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", False, "newBufferedWriter", "", "", "Argument[0]", "path-injection", "manual"] @@ -37,11 +37,6 @@ extensions: - ["java.nio.file", "Files", False, "write", "", "", "Argument[1]", "file-content-store", "manual"] - ["java.nio.file", "Files", False, "writeString", "", "", "Argument[0]", "path-injection", "manual"] - ["java.nio.file", "Files", False, "writeString", "", "", "Argument[1]", "file-content-store", "manual"] - - ["java.nio.file", "Files", True, "move", "(Path,Path,CopyOption[])", "", "Argument[1]", "path-injection", "ai-manual"] - - ["java.nio.file", "Files", True, "move", "(Path,Path,CopyOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - - ["java.nio.file", "Files", True, "delete", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] - - ["java.nio.file", "Files", True, "newInputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - - ["java.nio.file", "Files", True, "newOutputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "FileSystem", False, "getPath", "", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation - ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "request-forgery", "ai-manual"] diff --git a/java/ql/lib/ext/struts2.model.yml b/java/ql/lib/ext/struts2.model.yml new file mode 100644 index 00000000000..78e93e373f8 --- /dev/null +++ b/java/ql/lib/ext/struts2.model.yml @@ -0,0 +1,80 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "setValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "getValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "setParameter", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "trySetValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "getValueUsingOgnl", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "tryFindValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "findValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "findString", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlValueStack", False, "tryFindValueWhenExpressionIsNotNull", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlReflectionProvider", False, "getValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlReflectionProvider", False, "setValue", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlReflectionProvider", False, "setProperty", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlReflectionProvider", False, "setProperties", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperties", "(Map,Object)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperties", "(Map,Object,Map)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperties", "(Map,Object,Map,boolean)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperties", "(Map,Object,boolean)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperty", "(String,Object,Object,Map)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.ognl", "OgnlUtil", true, "setProperty", "(String,Object,Object,Map,boolean)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "LocalizedTextUtil", False, "findText", "", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "LocalizedTextUtil", False, "findText", "", "", "Argument[3]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "OgnlTextParser", False, "evaluate", "", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(String,ValueStack)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(String,ValueStack,ParsedValueEvaluator)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class,ParsedValueEvaluator)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char,String,ValueStack,Class,ParsedValueEvaluator,int)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char[],String,ValueStack,Class,ParsedValueEvaluator)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariables", "(char[],String,ValueStack,Class,ParsedValueEvaluator,int)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariablesCollection", "(String,ValueStack,boolean,ParsedValueEvaluator)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.util", "TextParseUtil", true, "translateVariablesCollection", "(char[],String,ValueStack,boolean,ParsedValueEvaluator,int)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "ActionSupport", true, "getFormatted", "(String,String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "ActionSupport", true, "getFormatted", "(String,String)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,List)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,List)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[2]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List)", "", "Argument[this]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[2]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,List,ValueStack)", "", "Argument[this]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String)", "", "Argument[2]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[2]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[])", "", "Argument[this]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[2]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String,String[],ValueStack)", "", "Argument[this]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String[])", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "getText", "(String,String[])", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "hasKey", "(String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2", "TextProvider", true, "hasKey", "(String)", "", "Argument[this]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "findString", "(String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "findValue", "(String,String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "getText", "(String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "isTrue", "(String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "makeSelectList", "(String,String,String,String)", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "makeSelectList", "(String,String,String,String)", "", "Argument[1]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "makeSelectList", "(String,String,String,String)", "", "Argument[2]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", true, "makeSelectList", "(String,String,String,String)", "", "Argument[3]", "ognl-injection", "manual"] + - ["org.apache.struts2.util", "StrutsUtil", True, "translateVariables", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["org.apache.struts2.views.jsp", "StrutsBodyTagSupport", False, "findPattern", "", "", "Argument[1]", "ognl-injection", "manual"] + - ["org.apache.struts2.views.jsp", "StrutsBodyTagSupport", False, "findString", "", "", "Argument[1]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.validator.validators", "ValidatorSupport", False, "parse", "", "", "Argument[0]", "ognl-injection", "manual"] + - ["com.opensymphony.xwork2.validator.validators", "ValidatorSupport", False, "getFieldValue", "", "", "Argument[0]", "ognl-injection", "manual"] diff --git a/java/ql/lib/ext/threatmodels/supported-threat-models.model.yml b/java/ql/lib/ext/threatmodels/supported-threat-models.model.yml new file mode 100644 index 00000000000..8c6c533228d --- /dev/null +++ b/java/ql/lib/ext/threatmodels/supported-threat-models.model.yml @@ -0,0 +1,7 @@ +extensions: + + - addsTo: + pack: codeql/java-all + extensible: supportedThreatModels + data: + - ["default"] # The "default" threat model is always included. diff --git a/java/ql/lib/ext/threatmodels/threat-model-grouping.model.yml b/java/ql/lib/ext/threatmodels/threat-model-grouping.model.yml new file mode 100644 index 00000000000..2b85b258b57 --- /dev/null +++ b/java/ql/lib/ext/threatmodels/threat-model-grouping.model.yml @@ -0,0 +1,23 @@ +extensions: + + - addsTo: + pack: codeql/java-all + extensible: threatModelGrouping + data: + # Default threat model + - ["remote", "default"] + - ["uri-path", "default"] + + # Android threat models + - ["android-external-storage-dir", "android"] + - ["contentprovider", "android"] + + # Remote threat models + - ["request", "remote"] + - ["response", "remote"] + + # Local threat models + - ["database", "local"] + - ["cli", "local"] + - ["environment", "local"] + - ["file", "local"] diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index dc92e3ce94d..666a6240d47 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/java-all -version: 0.7.1 +version: 0.7.2 groups: java dbscheme: config/semmlecode.dbscheme extractor: java library: true upgrades: upgrades dependencies: + codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} @@ -15,4 +16,5 @@ dataExtensions: - ext/*.model.yml - ext/generated/*.model.yml - ext/experimental/*.model.yml + - ext/threatmodels/*.model.yml warnOnImplicitThis: true diff --git a/java/ql/lib/semmle/code/java/Diagnostics.qll b/java/ql/lib/semmle/code/java/Diagnostics.qll index e6e16b4e07c..0134b32c5c0 100644 --- a/java/ql/lib/semmle/code/java/Diagnostics.qll +++ b/java/ql/lib/semmle/code/java/Diagnostics.qll @@ -9,6 +9,11 @@ class Diagnostic extends @diagnostic { /** Gets the compilation that generated this diagnostic. */ Compilation getCompilation() { diagnostic_for(this, result, _, _) } + /** Gets the compilation information for this diagnostic. */ + predicate getCompilationInfo(Compilation c, int fileNumber, int diagnosticNumber) { + diagnostic_for(this, c, fileNumber, diagnosticNumber) + } + /** * Gets the program that generated this diagnostic. */ diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 156cbbc0f93..6372b22c8f4 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -177,6 +177,11 @@ class TypeObjectInputStream extends RefType { TypeObjectInputStream() { this.hasQualifiedName("java.io", "ObjectInputStream") } } +/** The class `java.io.InputStream`. */ +class TypeInputStream extends RefType { + TypeInputStream() { this.hasQualifiedName("java.io", "InputStream") } +} + /** The class `java.nio.file.Paths`. */ class TypePaths extends Class { TypePaths() { this.hasQualifiedName("java.nio.file", "Paths") } diff --git a/java/ql/lib/semmle/code/java/Serializability.qll b/java/ql/lib/semmle/code/java/Serializability.qll index 72490118020..f665f663c7e 100644 --- a/java/ql/lib/semmle/code/java/Serializability.qll +++ b/java/ql/lib/semmle/code/java/Serializability.qll @@ -6,6 +6,7 @@ import java private import frameworks.jackson.JacksonSerializability private import frameworks.google.GsonSerializability private import frameworks.google.GoogleHttpClientApi +private import frameworks.struts.Struts2Serializability /** * A serializable field may be read without code referencing it, diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index a85df42eb55..1b1cb83e4f8 100644 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -1261,6 +1261,7 @@ predicate notHaveIntersection(RefType t1, RefType t2) { * Holds if there is a common (reflexive, transitive) subtype of the erased * types `t1` and `t2`. */ +pragma[nomagic] predicate erasedHaveIntersection(RefType t1, RefType t2) { exists(SrcRefType commonSub | commonSub.getASourceSupertype*() = t1 and commonSub.getASourceSupertype*() = t2 diff --git a/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll b/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll index 3676423ccbf..0f87cb7010c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll @@ -6,6 +6,8 @@ import java module DataFlow { - import semmle.code.java.dataflow.internal.DataFlow + private import semmle.code.java.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import semmle.code.java.dataflow.internal.DataFlowImpl1 } diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlowConfiguration.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlowConfiguration.qll new file mode 100644 index 00000000000..a3bd7d158c2 --- /dev/null +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlowConfiguration.qll @@ -0,0 +1,31 @@ +/** + * INTERNAL use only. This is an experimental API subject to change without notice. + * + * This module provides extensible predicates for configuring which kinds of MaD models + * are applicable to generic queries. + */ + +private import ExternalFlowExtensions + +/** + * Holds if the specified kind of source model is supported for the current query. + */ +extensible private predicate supportedThreatModels(string kind); + +/** + * Holds if the specified kind of source model is containted within the specified group. + */ +extensible private predicate threatModelGrouping(string kind, string group); + +/** + * Gets the threat models that are direct descendants of the specified kind/group. + */ +private string getChildThreatModel(string group) { threatModelGrouping(result, group) } + +/** + * Holds if the source model kind `kind` is relevant for generic queries + * under the current threat model configuration. + */ +predicate sourceModelKindConfig(string kind) { + exists(string group | supportedThreatModels(group) and kind = getChildThreatModel*(group)) +} diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll index f049a0cb37b..26f29076efc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll @@ -143,11 +143,10 @@ private class GuiceRequestParameterSource extends RemoteFlowSource { override string getSourceType() { result = "Guice request parameter" } } -private class Struts2ActionSupportClassFieldReadSource extends RemoteFlowSource { - Struts2ActionSupportClassFieldReadSource() { - exists(Struts2ActionSupportClass c | - c.getASetterMethod().getField() = this.asExpr().(FieldRead).getField() - ) +private class Struts2ActionSupportClassFieldSource extends RemoteFlowSource { + Struts2ActionSupportClassFieldSource() { + this.(DataFlow::FieldValueNode).getField() = + any(Struts2ActionSupportClass c).getASetterMethod().getField() } override string getSourceType() { result = "Struts2 ActionSupport field" } diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll index 1619965f0f0..fef69bec7fd 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll @@ -20,11 +20,11 @@ private module Frameworks { private import semmle.code.java.frameworks.Guice private import semmle.code.java.frameworks.IoJsonWebToken private import semmle.code.java.frameworks.jackson.JacksonSerializability + private import semmle.code.java.frameworks.InputStream private import semmle.code.java.frameworks.Properties private import semmle.code.java.frameworks.Protobuf private import semmle.code.java.frameworks.ratpack.RatpackExec private import semmle.code.java.frameworks.stapler.Stapler - private import semmle.code.java.JDK } /** diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll index c061f559251..53c0a83a536 100644 --- a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll @@ -757,7 +757,7 @@ private predicate baseBound(Expr e, int b, boolean upper) { or exists(Method read | e.(MethodAccess).getMethod().overrides*(read) and - read.getDeclaringType().hasQualifiedName("java.io", "InputStream") and + read.getDeclaringType() instanceof TypeInputStream and read.hasName("read") and read.getNumberOfParameters() = 0 | diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..30746706e31 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index b0de9745816..1975ac9781f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..2118572f779 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll index 1f1e9125845..5ddea224875 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides Java-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,10 @@ module Private { module Public { import DataFlowUtil } + +module JavaDataFlow implements InputSig { + import Private + import Public + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 216523023d9..ed4598c8bb6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -33,17 +33,17 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { } /** - * Holds if data can flow from `node1` to `node2` through a static field. + * Holds if data can flow from `node1` to `node2` through a field. */ -private predicate staticFieldStep(Node node1, Node node2) { +private predicate fieldStep(Node node1, Node node2) { exists(Field f | + // Taint fields through assigned values only if they're static f.isStatic() and f.getAnAssignedValue() = node1.asExpr() and node2.(FieldValueNode).getField() = f ) or exists(Field f, FieldRead fr | - f.isStatic() and node1.(FieldValueNode).getField() = f and fr.getField() = f and fr = node2.asExpr() and @@ -72,11 +72,11 @@ private predicate variableCaptureStep(Node node1, ExprNode node2) { } /** - * Holds if data can flow from `node1` to `node2` through a static field or + * Holds if data can flow from `node1` to `node2` through a field or * variable capture. */ predicate jumpStep(Node node1, Node node2) { - staticFieldStep(node1, node2) + fieldStep(node1, node2) or variableCaptureStep(node1, node2) or @@ -106,7 +106,7 @@ private predicate instanceFieldAssign(Expr src, FieldAccess fa) { * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. */ -predicate storeStep(Node node1, Content f, Node node2) { +predicate storeStep(Node node1, ContentSet f, Node node2) { exists(FieldAccess fa | instanceFieldAssign(node1.asExpr(), fa) and node2.(PostUpdateNode).getPreUpdateNode() = getFieldQualifier(fa) and @@ -124,7 +124,7 @@ predicate storeStep(Node node1, Content f, Node node2) { * Thus, `node1` references an object with a field `f` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content f, Node node2) { +predicate readStep(Node node1, ContentSet f, Node node2) { exists(FieldRead fr | node1 = getFieldQualifier(fr) and fr.getField() = f.(FieldContent).getField() and @@ -156,7 +156,7 @@ predicate readStep(Node node1, Content f, Node node2) { * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { exists(FieldAccess fa | instanceFieldAssign(_, fa) and n = getFieldQualifier(fa) and @@ -207,47 +207,25 @@ DataFlowType getNodeType(Node n) { } /** Gets a string representation of a type returned by `getErasedRepr`. */ -string ppReprType(Type t) { +string ppReprType(DataFlowType t) { if t.(BoxedType).getPrimitiveType().getName() = "double" then result = "Number" else result = t.toString() } -private predicate canContainBool(Type t) { - t instanceof BooleanType or - any(BooleanType b).(RefType).getASourceSupertype+() = t -} - /** * Holds if `t1` and `t2` are compatible, that is, whether data can flow from * a node of type `t1` to a node of type `t2`. */ -pragma[inline] -predicate compatibleTypes(Type t1, Type t2) { - exists(Type e1, Type e2 | - e1 = getErasedRepr(t1) and - e2 = getErasedRepr(t2) - | - // Because of `getErasedRepr`, `erasedHaveIntersection` is a sufficient - // compatibility check, but `conContainBool` is kept as a dummy disjunct - // to get the proper join-order. - erasedHaveIntersection(e1, e2) - or - canContainBool(e1) and canContainBool(e2) - ) -} +bindingset[t1, t2] +pragma[inline_late] +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { erasedHaveIntersection(t1, t2) } /** A node that performs a type cast. */ class CastNode extends ExprNode { CastNode() { this.getExpr() instanceof CastingExpr } } -/** - * Holds if `n` should never be skipped over in the `PathGraph` and in path - * explanations. - */ -predicate neverSkipInPathGraph(Node n) { none() } - private newtype TDataFlowCallable = TSrcCallable(Callable c) or TSummarizedCallable(SummarizedCallable c) or @@ -381,8 +359,6 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { ) } -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index c992f92ee8a..6f8dbb1771b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -239,7 +239,7 @@ private class BulkData extends RefType { this.(Array).getElementType().(PrimitiveType).hasName(["byte", "char"]) or exists(RefType t | this.getASourceSupertype*() = t | - t.hasQualifiedName("java.io", "InputStream") or + t instanceof TypeInputStream or t.hasQualifiedName("java.nio", "ByteBuffer") or t.hasQualifiedName("java.lang", "Readable") or t.hasQualifiedName("java.io", "DataInput") or @@ -259,7 +259,7 @@ private class BulkData extends RefType { private predicate inputStreamWrapper(Constructor c, int argi) { not c.fromSource() and c.getParameterType(argi) instanceof BulkData and - c.getDeclaringType().getASourceSupertype+().hasQualifiedName("java.io", "InputStream") + c.getDeclaringType().getASourceSupertype+() instanceof TypeInputStream } /** An object construction that preserves the data flow status of any of its arguments. */ diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index 4b880542229..64f26685b68 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -102,6 +102,8 @@ private module Dispatch { or t instanceof Interface and not t.fromSource() or + t instanceof TypeInputStream + or t.hasQualifiedName("java.io", "Serializable") or t.hasQualifiedName("java.lang", "Iterable") diff --git a/java/ql/lib/semmle/code/java/frameworks/InputStream.qll b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll new file mode 100644 index 00000000000..8f37ecc24ea --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll @@ -0,0 +1,90 @@ +/** Provides definitions related to `java.io.InputStream`. */ + +import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.SSA +private import semmle.code.java.dataflow.TaintTracking + +/** + * A jump taint step from an update of the `bytes[]` parameter in an override of the `InputStream.read` method + * to a class instance expression of the type extending `InputStream`. + * + * This models how a subtype of `InputStream` could be tainted by the definition of its methods, which will + * normally only happen in nested classes. + */ +private class InputStreamWrapperCapturedJumpStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(InputStreamRead m, NestedClass wrapper | + m.getDeclaringType() = wrapper and + wrapper.getASourceSupertype+() instanceof TypeInputStream + | + n1.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess() and + n2.asExpr() + .(ClassInstanceExpr) + .getConstructedType() + .getASourceSupertype*() + .getSourceDeclaration() = wrapper + ) + } +} + +/** + * A local taint step from the definition of a captured variable, the capturer of which + * updates the `bytes[]` parameter in an override of the `InputStream.read` method, + * to a class instance expression of the type extending `InputStream`. + * + * This models how a subtype of `InputStream` could be tainted by capturing tainted variables in + * the definition of its methods. + */ +private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(InputStreamRead m, NestedClass wrapper, SsaVariable captured, SsaImplicitInit capturer | + wrapper.getASourceSupertype+() instanceof TypeInputStream and + m.getDeclaringType() = wrapper and + capturer.captures(captured) and + TaintTracking::localTaint(DataFlow::exprNode(capturer.getAFirstUse()), + any(DataFlow::PostUpdateNode pun | + pun.getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess() + )) and + n2.asExpr() + .(ClassInstanceExpr) + .getConstructedType() + .getASourceSupertype*() + .getSourceDeclaration() = wrapper + | + n1.asExpr() = captured.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() + or + captured.(SsaImplicitInit).isParameterDefinition(n1.asParameter()) + ) + } +} + +/** + * A taint step from an `InputStream` argument of the constructor of an `InputStream` subtype + * to the call of the constructor, only if the argument is assigned to a class field. + * + * This models how it's assumed that an `InputStream` wrapper is tainted by the wrapped stream, + * and is a workaround to low `fieldFlowBranchLimit`s in dataflow configurations. + */ +private class InputStreamWrapperConstructorStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(ClassInstanceExpr cc, Argument a, AssignExpr ae, int pos | + cc.getConstructedType().getASourceSupertype+() instanceof TypeInputStream and + cc.getArgument(pragma[only_bind_into](pos)) = a and + cc.getCallee().getParameter(pragma[only_bind_into](pos)).getAnAccess() = ae.getRhs() and + ae.getDest().(FieldWrite).getField().getType().(RefType).getASourceSupertype*() instanceof + TypeInputStream + | + n1.asExpr() = a and + n2.asExpr() = cc + ) + } +} + +private class InputStreamRead extends Method { + InputStreamRead() { + this.hasName("read") and + this.getDeclaringType().getASourceSupertype*() instanceof TypeInputStream + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll index 8df603c5d6a..461a7dc8208 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll @@ -317,7 +317,7 @@ class SystemSetInputStreamMethod extends Method { SystemSetInputStreamMethod() { this.hasName("setIn") and this.getNumberOfParameters() = 1 and - this.getParameter(0).getType().(RefType).hasQualifiedName("java.io", "InputStream") and + this.getParameter(0).getType() instanceof TypeInputStream and this.getDeclaringType() .getAnAncestor() .getSourceDeclaration() diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll index d40d1608969..f2611d6e2d1 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll @@ -237,7 +237,7 @@ class SpringRequestMappingParameter extends Parameter { private predicate isExplicitlyTaintedInput() { // InputStream or Reader parameters allow access to the body of a request - this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "InputStream") or + this.getType().(RefType).getAnAncestor() instanceof TypeInputStream or this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader") or // The SpringServletInputAnnotations allow access to the URI, request parameters, cookie values and the body of the request this.getAnAnnotation() instanceof SpringServletInputAnnotation or diff --git a/java/ql/lib/semmle/code/java/frameworks/struts/Struts2Serializability.qll b/java/ql/lib/semmle/code/java/frameworks/struts/Struts2Serializability.qll new file mode 100644 index 00000000000..cb8b876be7a --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/struts/Struts2Serializability.qll @@ -0,0 +1,52 @@ +/** + * Provides classes and predicates for working with objects bound from Http requests in the context of + * the Struts2 web framework. + */ + +import java +private import semmle.code.java.Serializability +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.frameworks.struts.StrutsActions + +/** A type whose values may be unmarshalled from an Http request by the Struts2 framework. */ +abstract class Struts2DeserializableType extends Type { } + +/** A type whose values are explicitly unmarshalled by from an Http request by the Struts2 framework. */ +private class ExplicitlyReadStruts2DeserializableType extends Struts2DeserializableType { + ExplicitlyReadStruts2DeserializableType() { + exists(Struts2ActionSupportClass c | + usesType(c.getASetterMethod().getField().getType(), this) and + not this instanceof TypeClass and + not this instanceof TypeObject + ) + } +} + +/** A type used in a `Struts2ActionField` declaration. */ +private class FieldReferencedStruts2DeserializableType extends Struts2DeserializableType { + FieldReferencedStruts2DeserializableType() { + exists(Struts2ActionField f | usesType(f.getType(), this)) + } +} + +/** A field that may be unmarshalled from an Http request using the Struts2 framework. */ +private class Struts2ActionField extends DeserializableField { + Struts2ActionField() { + exists(Struts2DeserializableType superType | + superType = this.getDeclaringType().getAnAncestor() and + not superType instanceof TypeObject and + superType.fromSource() and + ( + this.isPublic() + or + exists(SetterMethod setter | setter.getField() = this and setter.isPublic()) + ) + ) + } +} + +/** A field that should convey the taint from its qualifier to itself. */ +private class Struts2ActionFieldInheritTaint extends DataFlow::FieldContent, TaintInheritingContent { + Struts2ActionFieldInheritTaint() { this.getField() instanceof Struts2ActionField } +} diff --git a/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll b/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll index 2cd4dcb7fe7..38f5ad08b7e 100644 --- a/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll +++ b/java/ql/lib/semmle/code/java/security/MaybeBrokenCryptoAlgorithmQuery.qll @@ -34,15 +34,6 @@ private predicate objectToString(MethodAccess ma) { ) } -private class StringContainer extends RefType { - StringContainer() { - this instanceof TypeString or - this instanceof StringBuildingType or - this.hasQualifiedName("java.util", "StringTokenizer") or - this.(Array).getComponentType() instanceof StringContainer - } -} - /** * A taint-tracking configuration to reason about the use of potentially insecure cryptographic algorithms. */ @@ -53,7 +44,7 @@ module InsecureCryptoConfig implements DataFlow::ConfigSig { predicate isBarrier(DataFlow::Node n) { objectToString(n.asExpr()) or - not n.getType().getErasure() instanceof StringContainer + n.getType().getErasure() instanceof TypeObject } } diff --git a/java/ql/lib/semmle/code/java/security/RandomDataSource.qll b/java/ql/lib/semmle/code/java/security/RandomDataSource.qll index e52c6a3b7eb..29c980beb5f 100644 --- a/java/ql/lib/semmle/code/java/security/RandomDataSource.qll +++ b/java/ql/lib/semmle/code/java/security/RandomDataSource.qll @@ -103,7 +103,7 @@ class StdlibRandomSource extends RandomDataSource { } override Expr getOutput() { - if m.hasName("getBytes") then result = this.getArgument(0) else result = this + if m.hasName("nextBytes") then result = this.getArgument(0) else result = this } } diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index d2517609d01..9f6f4717c1f 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.7.2 + +### Minor Analysis Improvements + +* The sanitizer in `java/potentially-weak-cryptographic-algorithm` has been improved, so the query may yield additional results. + ## 0.7.1 ### Minor Analysis Improvements diff --git a/java/ql/src/Telemetry/AutomodelAlertSinkUtil.qll b/java/ql/src/Telemetry/AutomodelAlertSinkUtil.qll index 4196ba8add3..8082a1efc5e 100644 --- a/java/ql/src/Telemetry/AutomodelAlertSinkUtil.qll +++ b/java/ql/src/Telemetry/AutomodelAlertSinkUtil.qll @@ -1,6 +1,5 @@ private import java private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow -private import semmle.code.java.dataflow.internal.DataFlow private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.security.RequestForgeryConfig private import semmle.code.java.security.CommandLineQuery diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll index 51e786eebdc..e14c18ace32 100644 --- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll @@ -12,7 +12,6 @@ private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummary private import semmle.code.java.security.ExternalAPIs as ExternalAPIs private import semmle.code.java.Expr as Expr private import semmle.code.java.security.QueryInjection -private import semmle.code.java.security.RequestForgery private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions private import AutomodelJavaUtil as AutomodelJavaUtil private import semmle.code.java.security.PathSanitizer as PathSanitizer @@ -26,7 +25,17 @@ newtype JavaRelatedLocationType = CallContext() * A class representing nodes that are arguments to calls. */ private class ArgumentNode extends DataFlow::Node { - ArgumentNode() { this.asExpr() = [any(Call c).getAnArgument(), any(Call c).getQualifier()] } + Call c; + + ArgumentNode() { + exists(Argument arg | this.asExpr() = arg and not arg.isVararg() and c = arg.getCall()) + or + this.(DataFlow::ImplicitVarargsArray).getCall() = c + or + this = DataFlow::getInstanceArgument(c) + } + + Call getCall() { result = c } } /** @@ -67,13 +76,13 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2; - predicate isSink(Endpoint e, string kind) { + predicate isSink(Endpoint e, string kind, string provenance) { exists(string package, string type, string name, string signature, string ext, string input | sinkSpec(e, package, type, name, signature, ext, input) and - ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, _) + ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, provenance) ) or - isCustomSink(e, kind) + isCustomSink(e, kind) and provenance = "custom-sink" } predicate isNeutral(Endpoint e) { @@ -136,10 +145,6 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable: * should be empty. */ private predicate isCustomSink(Endpoint e, string kind) { - e.asExpr() instanceof ArgumentToExec and kind = "command injection" - or - e instanceof RequestForgerySink and kind = "request forgery" - or e instanceof QueryInjectionSink and kind = "sql" } @@ -200,7 +205,7 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not ApplicationCandidatesImpl::isSink(e, _) and + not ApplicationCandidatesImpl::isSink(e, _, _) and ApplicationModeGetCallable::getCallable(e).getName().matches("is%") and ApplicationModeGetCallable::getCallable(e).getReturnType() instanceof BooleanType } @@ -218,7 +223,7 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not ApplicationCandidatesImpl::isSink(e, _) and + not ApplicationCandidatesImpl::isSink(e, _, _) and exists(Callable callable | callable = ApplicationModeGetCallable::getCallable(e) and callable.getName().toLowerCase() = ["exists", "notexists"] and @@ -313,7 +318,8 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter /** * A negative characteristic that indicates that an endpoint is a non-sink argument to a method whose sinks have already - * been modeled. + * been modeled _manually_. This is restricted to manual sinks only, because only during the manual process do we have + * the expectation that all sinks present in a method have been considered. * * WARNING: These endpoints should not be used as negative samples for training, because some sinks may have been missed * when the method was modeled. Specifically, as we start using ATM to merge in new declarations, we can be less sure @@ -324,14 +330,14 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter private class OtherArgumentToModeledMethodCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic { OtherArgumentToModeledMethodCharacteristic() { - this = "other argument to a method that has already been modeled" + this = "other argument to a method that has already been modeled manually" } override predicate appliesToEndpoint(Endpoint e) { - not ApplicationCandidatesImpl::isSink(e, _) and - exists(DataFlow::Node otherSink | - ApplicationCandidatesImpl::isSink(otherSink, _) and - e.asExpr() = otherSink.asExpr().(Argument).getCall().getAnArgument() and + not ApplicationCandidatesImpl::isSink(e, _, _) and + exists(Endpoint otherSink | + ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and + e.getCall() = otherSink.getCall() and e != otherSink ) } diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql index 4e40fadd750..4940b4a741f 100644 --- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql +++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql @@ -64,7 +64,7 @@ where // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in // overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been // modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it. - not CharacteristicsImpl::isSink(endpoint, _) and + not CharacteristicsImpl::isSink(endpoint, _, _) and meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and // The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be // a non-sink, and we surface only endpoints that have at least one such sink type. diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll index bc5c3b59a91..02d945437e3 100644 --- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll @@ -23,6 +23,66 @@ newtype JavaRelatedLocationType = MethodDoc() or ClassDoc() +newtype TFrameworkModeEndpoint = + TExplicitParameter(Parameter p) or + TQualifier(Callable c) + +/** + * A framework mode endpoint. + */ +abstract class FrameworkModeEndpoint extends TFrameworkModeEndpoint { + /** + * Returns the parameter index of the endpoint. + */ + abstract int getIndex(); + + /** + * Returns the name of the parameter of the endpoint. + */ + abstract string getParamName(); + + /** + * Returns the callable that contains the endpoint. + */ + abstract Callable getEnclosingCallable(); + + abstract Top asTop(); + + string toString() { result = this.asTop().toString() } + + Location getLocation() { result = this.asTop().getLocation() } +} + +class ExplicitParameterEndpoint extends FrameworkModeEndpoint, TExplicitParameter { + Parameter param; + + ExplicitParameterEndpoint() { this = TExplicitParameter(param) and param.fromSource() } + + override int getIndex() { result = param.getPosition() } + + override string getParamName() { result = param.getName() } + + override Callable getEnclosingCallable() { result = param.getCallable() } + + override Top asTop() { result = param } +} + +class QualifierEndpoint extends FrameworkModeEndpoint, TQualifier { + Callable callable; + + QualifierEndpoint() { + this = TQualifier(callable) and not callable.isStatic() and callable.fromSource() + } + + override int getIndex() { result = -1 } + + override string getParamName() { result = "this" } + + override Callable getEnclosingCallable() { result = callable } + + override Top asTop() { result = callable } +} + /** * A candidates implementation for framework mode. * @@ -33,7 +93,7 @@ newtype JavaRelatedLocationType = */ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { // for documentation of the implementations here, see the QLDoc in the CandidateSig signature module. - class Endpoint = DataFlow::ParameterNode; + class Endpoint = FrameworkModeEndpoint; class EndpointType = AutomodelEndpointTypes::EndpointType; @@ -46,14 +106,14 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { // Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact. predicate isSanitizer(Endpoint e, EndpointType t) { none() } - RelatedLocation asLocation(Endpoint e) { result = e.asParameter() } + RelatedLocation asLocation(Endpoint e) { result = e.asTop() } predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2; - predicate isSink(Endpoint e, string kind) { + predicate isSink(Endpoint e, string kind, string provenance) { exists(string package, string type, string name, string signature, string ext, string input | sinkSpec(e, package, type, name, signature, ext, input) and - ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, _) + ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, provenance) ) } @@ -70,9 +130,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { FrameworkModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and signature = ExternalFlow::paramsString(FrameworkModeGetCallable::getCallable(e)) and ext = "" and - exists(int paramIdx | e.isParameterOf(_, paramIdx) | - input = AutomodelJavaUtil::getArgumentForIndex(paramIdx) - ) + input = AutomodelJavaUtil::getArgumentForIndex(e.getIndex()) } /** @@ -124,16 +182,13 @@ class FrameworkModeMetadataExtractor extends string { Endpoint e, string package, string type, string subtypes, string name, string signature, string input, string parameterName ) { - exists(Callable callable, int paramIdx | - e.asParameter() = callable.getParameter(paramIdx) and - input = AutomodelJavaUtil::getArgumentForIndex(paramIdx) and - package = callable.getDeclaringType().getPackage().getName() and - type = callable.getDeclaringType().getErasure().(RefType).nestedName() and - subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and - name = callable.getName() and - parameterName = e.asParameter().getName() and - signature = ExternalFlow::paramsString(callable) - ) + parameterName = e.getParamName() and + name = e.getEnclosingCallable().getName() and + input = AutomodelJavaUtil::getArgumentForIndex(e.getIndex()) and + package = e.getEnclosingCallable().getDeclaringType().getPackage().getName() and + type = e.getEnclosingCallable().getDeclaringType().getErasure().(RefType).nestedName() and + subtypes = AutomodelJavaUtil::considerSubtypes(e.getEnclosingCallable()).toString() and + signature = ExternalFlow::paramsString(e.getEnclosingCallable()) } } @@ -154,7 +209,7 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not FrameworkCandidatesImpl::isSink(e, _) and + not FrameworkCandidatesImpl::isSink(e, _, _) and FrameworkModeGetCallable::getCallable(e).getName().matches("is%") and FrameworkModeGetCallable::getCallable(e).getReturnType() instanceof BooleanType } @@ -172,7 +227,7 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not FrameworkCandidatesImpl::isSink(e, _) and + not FrameworkCandidatesImpl::isSink(e, _, _) and exists(Callable callable | callable = FrameworkModeGetCallable::getCallable(e) and callable.getName().toLowerCase() = ["exists", "notexists"] and @@ -201,7 +256,7 @@ private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingTo NotAModelApiParameter() { this = "not a model API parameter" } override predicate appliesToEndpoint(Endpoint e) { - not exists(ModelExclusions::ModelApi api | api.getAParameter() = e.asParameter()) + not e.getEnclosingCallable() instanceof ModelExclusions::ModelApi } } diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql index 4186d1c17b9..e66af08707c 100644 --- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql +++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql @@ -28,7 +28,7 @@ where // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in // overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been // modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it. - not CharacteristicsImpl::isSink(endpoint, _) and + not CharacteristicsImpl::isSink(endpoint, _, _) and meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, parameterName) and // The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be // a non-sink, and we surface only endpoints that have at least one such sink type. diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll index b077f77deb9..c1a9a14a10a 100644 --- a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll @@ -58,9 +58,9 @@ signature module CandidateSig { predicate isSanitizer(Endpoint e, EndpointType t); /** - * Holds if `e` is a sink with the label `kind`. + * Holds if `e` is a sink with the label `kind`, and provenance `provenance`. */ - predicate isSink(Endpoint e, string kind); + predicate isSink(Endpoint e, string kind, string provenance); /** * Holds if `e` is not a sink of any kind. @@ -87,7 +87,7 @@ signature module CandidateSig { * implementations of endpoint characteristics exported by this module. */ module SharedCharacteristics { - predicate isSink = Candidate::isSink/2; + predicate isSink = Candidate::isSink/3; predicate isNeutral = Candidate::isNeutral/1; @@ -282,7 +282,9 @@ module SharedCharacteristics { this = madKind + "-characteristic" } - override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madKind) } + override predicate appliesToEndpoint(Candidate::Endpoint e) { + Candidate::isSink(e, madKind, _) + } override Candidate::EndpointType getSinkType() { result = endpointType } } @@ -301,7 +303,7 @@ module SharedCharacteristics { * analyzed. */ private class IsSanitizerCharacteristic extends NotASinkCharacteristic { - IsSanitizerCharacteristic() { this = "external" } + IsSanitizerCharacteristic() { this = "known sanitizer" } override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) } } diff --git a/java/ql/src/change-notes/released/0.7.2.md b/java/ql/src/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..98e5f74f27d --- /dev/null +++ b/java/ql/src/change-notes/released/0.7.2.md @@ -0,0 +1,5 @@ +## 0.7.2 + +### Minor Analysis Improvements + +* The sanitizer in `java/potentially-weak-cryptographic-algorithm` has been improved, so the query may yield additional results. diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.java b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.java new file mode 100644 index 00000000000..12c7e952d99 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.java @@ -0,0 +1,9 @@ +class Test { + public static void main(String[] args) { + String script = System.getenv("SCRIPTNAME"); + if (script != null) { + // BAD: The script to be executed by /bin/sh is controlled by the user. + Runtime.getRuntime().exec(new String[]{"/bin/sh", script}); + } + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qhelp b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qhelp new file mode 100644 index 00000000000..820f5b71405 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qhelp @@ -0,0 +1,41 @@ + + + +

Code that passes remote user input to an arugment of a call of Runtime.exec that +executes a scripting executable will allow the user to execute malicious code.

+ +
+ + +

If possible, use hard-coded string literals to specify the command or script to run, +or library to load. Instead of passing the user input directly to the +process or library function, examine the user input and then choose +among hard-coded string literals.

+ +

If the applicable libraries or commands cannot be determined at +compile time, then add code to verify that the user input string is +safe before using it.

+ +
+ + +

The following example shows code that takes a shell script that can be changed +maliciously by a user, and passes it straight to the array going into Runtime.exec +without examining it first.

+ + + +
+ + +
  • +OWASP: +Command Injection. +
  • +
  • SEI CERT Oracle Coding Standard for Java: + IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method.
  • + +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.ql b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.ql new file mode 100644 index 00000000000..8c020b6f22b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.ql @@ -0,0 +1,25 @@ +/** + * @name Command Injection into Runtime.exec() with dangerous command + * @description High sensitvity and precision version of java/command-line-injection, designed to find more cases of command injection in rare cases that the default query does not find + * @kind path-problem + * @problem.severity error + * @security-severity 6.1 + * @precision high + * @id java/command-line-injection-extra + * @tags security + * experimental + * external/cwe/cwe-078 + */ + +import CommandInjectionRuntimeExec +import ExecUserFlow::PathGraph + +class RemoteSource extends Source instanceof RemoteFlowSource { } + +from + ExecUserFlow::PathNode source, ExecUserFlow::PathNode sink, DataFlow::Node sourceCmd, + DataFlow::Node sinkCmd +where callIsTaintedByUserInputAndDangerousCommand(source, sink, sourceCmd, sinkCmd) +select sink, source, sink, + "Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@'", + sourceCmd, sourceCmd.toString(), source.getNode(), source.toString() diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qll b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qll new file mode 100644 index 00000000000..7aeef61b58b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.qll @@ -0,0 +1,108 @@ +import java +import semmle.code.java.frameworks.javaee.ejb.EJBRestrictions +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources + +module ExecCmdFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr().(CompileTimeConstantExpr).getStringValue() instanceof UnSafeExecutable + } + + predicate isSink(DataFlow::Node sink) { + exists(MethodAccess call | + call.getMethod() instanceof RuntimeExecMethod and + sink.asExpr() = call.getArgument(0) and + sink.asExpr().getType() instanceof Array + ) + } + + predicate isBarrier(DataFlow::Node node) { + node instanceof AssignToNonZeroIndex or + node instanceof ArrayInitAtNonZeroIndex or + node instanceof StreamConcatAtNonZeroIndex or + node.getType() instanceof PrimitiveType or + node.getType() instanceof BoxedType + } +} + +/** Tracks flow of unvalidated user input that is used in Runtime.Exec */ +module ExecCmdFlow = TaintTracking::Global; + +abstract class Source extends DataFlow::Node { } + +module ExecUserFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isSink(DataFlow::Node sink) { + exists(MethodAccess call | + call.getMethod() instanceof RuntimeExecMethod and + sink.asExpr() = call.getArgument(_) and + sink.asExpr().getType() instanceof Array + ) + } + + predicate isBarrier(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or + node.getType() instanceof BoxedType + } +} + +/** Tracks flow of unvalidated user input that is used in Runtime.Exec */ +module ExecUserFlow = TaintTracking::Global; + +// array[3] = node +class AssignToNonZeroIndex extends DataFlow::Node { + AssignToNonZeroIndex() { + exists(AssignExpr assign, ArrayAccess access | + assign.getDest() = access and + access.getIndexExpr().(IntegerLiteral).getValue().toInt() != 0 and + assign.getSource() = this.asExpr() + ) + } +} + +// String[] array = {"a", "b, "c"}; +class ArrayInitAtNonZeroIndex extends DataFlow::Node { + ArrayInitAtNonZeroIndex() { + exists(ArrayInit init, int index | + init.getInit(index) = this.asExpr() and + index != 0 + ) + } +} + +// Stream.concat(Arrays.stream(array_1), Arrays.stream(array_2)) +class StreamConcatAtNonZeroIndex extends DataFlow::Node { + StreamConcatAtNonZeroIndex() { + exists(MethodAccess call, int index | + call.getMethod().getQualifiedName() = "java.util.stream.Stream.concat" and + call.getArgument(index) = this.asExpr() and + index != 0 + ) + } +} + +// list of executables that execute their arguments +// TODO: extend with data extensions +class UnSafeExecutable extends string { + bindingset[this] + UnSafeExecutable() { + this.regexpMatch("^(|.*/)([a-z]*sh|javac?|python.*|perl|[Pp]ower[Ss]hell|php|node|deno|bun|ruby|osascript|cmd|Rscript|groovy)(\\.exe)?$") and + not this = "netsh.exe" + } +} + +predicate callIsTaintedByUserInputAndDangerousCommand( + ExecUserFlow::PathNode source, ExecUserFlow::PathNode sink, DataFlow::Node sourceCmd, + DataFlow::Node sinkCmd +) { + exists(MethodAccess call | + call.getMethod() instanceof RuntimeExecMethod and + // this is a command-accepting call to exec, e.g. rt.exec(new String[]{"/bin/sh", ...}) + ExecCmdFlow::flow(sourceCmd, sinkCmd) and + sinkCmd.asExpr() = call.getArgument(0) and + // it is tainted by untrusted user input + ExecUserFlow::flowPath(source, sink) and + sink.getNode().asExpr() = call.getArgument(0) + ) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.qhelp b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.qhelp new file mode 100644 index 00000000000..be2b7aac973 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.qhelp @@ -0,0 +1,41 @@ + + + +

    Code that passes local user input to an arugment of a call of Runtime.exec that +executes a scripting executable will allow the user to execute malicious code.

    + +
    + + +

    If possible, use hard-coded string literals to specify the command or script to run, +or library to load. Instead of passing the user input directly to the +process or library function, examine the user input and then choose +among hard-coded string literals.

    + +

    If the applicable libraries or commands cannot be determined at +compile time, then add code to verify that the user input string is +safe before using it.

    + +
    + + +

    The following example shows code that takes a shell script that can be changed +maliciously by a user, and passes it straight to the array going into Runtime.exec +without examining it first.

    + + + +
    + + +
  • +OWASP: +Command Injection. +
  • +
  • SEI CERT Oracle Coding Standard for Java: + IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method.
  • + +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.ql b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.ql new file mode 100644 index 00000000000..037b331609d --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.ql @@ -0,0 +1,26 @@ +/** + * @name Command Injection into Runtime.exec() with dangerous command + * @description High sensitvity and precision version of java/command-line-injection, designed to find more cases of command injection in rare cases that the default query does not find + * @kind path-problem + * @problem.severity error + * @security-severity 6.1 + * @precision high + * @id java/command-line-injection-extra-local + * @tags security + * experimental + * local + * external/cwe/cwe-078 + */ + +import CommandInjectionRuntimeExec +import ExecUserFlow::PathGraph + +class LocalSource extends Source instanceof LocalUserInput { } + +from + ExecUserFlow::PathNode source, ExecUserFlow::PathNode sink, DataFlow::Node sourceCmd, + DataFlow::Node sinkCmd +where callIsTaintedByUserInputAndDangerousCommand(source, sink, sourceCmd, sinkCmd) +select sink, source, sink, + "Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@'", + sourceCmd, sourceCmd.toString(), source.getNode(), source.toString() diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 2a7f20df88b..9eb6c56e6bd 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.7.1 +version: 0.7.2 groups: - java - queries diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.expected b/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.expected new file mode 100644 index 00000000000..0baff0f6f21 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.expected @@ -0,0 +1,41 @@ +edges +| RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:22:67:22:72 | script : String | +| RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:25:66:25:71 | script : String | +| RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:31:36:31:41 | script : String | +| RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:38:52:38:57 | script : String | +| RuntimeExecTest.java:22:43:22:73 | {...} : String[] [[]] : String | RuntimeExecTest.java:22:43:22:73 | new String[] | +| RuntimeExecTest.java:22:67:22:72 | script : String | RuntimeExecTest.java:22:43:22:73 | {...} : String[] [[]] : String | +| RuntimeExecTest.java:25:42:25:72 | {...} : String[] [[]] : String | RuntimeExecTest.java:26:43:26:55 | commandArray1 | +| RuntimeExecTest.java:25:66:25:71 | script : String | RuntimeExecTest.java:25:42:25:72 | {...} : String[] [[]] : String | +| RuntimeExecTest.java:31:17:31:29 | commandArray2 [post update] : String[] [[]] : String | RuntimeExecTest.java:32:43:32:55 | commandArray2 | +| RuntimeExecTest.java:31:36:31:41 | script : String | RuntimeExecTest.java:31:17:31:29 | commandArray2 [post update] : String[] [[]] : String | +| RuntimeExecTest.java:36:21:39:21 | concat(...) : Stream [] : String | RuntimeExecTest.java:36:21:39:44 | toArray(...) : String[] [[]] : String | +| RuntimeExecTest.java:36:21:39:44 | toArray(...) : String[] [[]] : String | RuntimeExecTest.java:36:21:39:44 | toArray(...) | +| RuntimeExecTest.java:38:25:38:59 | stream(...) : Stream [] : String | RuntimeExecTest.java:36:21:39:21 | concat(...) : Stream [] : String | +| RuntimeExecTest.java:38:39:38:58 | new String[] : String[] [[]] : String | RuntimeExecTest.java:38:25:38:59 | stream(...) : Stream [] : String | +| RuntimeExecTest.java:38:39:38:58 | {...} : String[] [[]] : String | RuntimeExecTest.java:38:39:38:58 | new String[] : String[] [[]] : String | +| RuntimeExecTest.java:38:52:38:57 | script : String | RuntimeExecTest.java:38:39:38:58 | {...} : String[] [[]] : String | +nodes +| RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | semmle.label | getenv(...) : String | +| RuntimeExecTest.java:22:43:22:73 | new String[] | semmle.label | new String[] | +| RuntimeExecTest.java:22:43:22:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| RuntimeExecTest.java:22:67:22:72 | script : String | semmle.label | script : String | +| RuntimeExecTest.java:25:42:25:72 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| RuntimeExecTest.java:25:66:25:71 | script : String | semmle.label | script : String | +| RuntimeExecTest.java:26:43:26:55 | commandArray1 | semmle.label | commandArray1 | +| RuntimeExecTest.java:31:17:31:29 | commandArray2 [post update] : String[] [[]] : String | semmle.label | commandArray2 [post update] : String[] [[]] : String | +| RuntimeExecTest.java:31:36:31:41 | script : String | semmle.label | script : String | +| RuntimeExecTest.java:32:43:32:55 | commandArray2 | semmle.label | commandArray2 | +| RuntimeExecTest.java:36:21:39:21 | concat(...) : Stream [] : String | semmle.label | concat(...) : Stream [] : String | +| RuntimeExecTest.java:36:21:39:44 | toArray(...) | semmle.label | toArray(...) | +| RuntimeExecTest.java:36:21:39:44 | toArray(...) : String[] [[]] : String | semmle.label | toArray(...) : String[] [[]] : String | +| RuntimeExecTest.java:38:25:38:59 | stream(...) : Stream [] : String | semmle.label | stream(...) : Stream [] : String | +| RuntimeExecTest.java:38:39:38:58 | new String[] : String[] [[]] : String | semmle.label | new String[] : String[] [[]] : String | +| RuntimeExecTest.java:38:39:38:58 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| RuntimeExecTest.java:38:52:38:57 | script : String | semmle.label | script : String | +subpaths +#select +| RuntimeExecTest.java:22:43:22:73 | new String[] | RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:22:43:22:73 | new String[] | Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@' | RuntimeExecTest.java:22:56:22:64 | "/bin/sh" | "/bin/sh" | RuntimeExecTest.java:17:25:17:51 | getenv(...) | getenv(...) : String | +| RuntimeExecTest.java:26:43:26:55 | commandArray1 | RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:26:43:26:55 | commandArray1 | Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@' | RuntimeExecTest.java:25:55:25:63 | "/bin/sh" | "/bin/sh" | RuntimeExecTest.java:17:25:17:51 | getenv(...) | getenv(...) : String | +| RuntimeExecTest.java:32:43:32:55 | commandArray2 | RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:32:43:32:55 | commandArray2 | Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@' | RuntimeExecTest.java:30:36:30:44 | "/bin/sh" | "/bin/sh" | RuntimeExecTest.java:17:25:17:51 | getenv(...) | getenv(...) : String | +| RuntimeExecTest.java:36:21:39:44 | toArray(...) | RuntimeExecTest.java:17:25:17:51 | getenv(...) : String | RuntimeExecTest.java:36:21:39:44 | toArray(...) | Call to dangerous java.lang.Runtime.exec() with command '$@' with arg from untrusted input '$@' | RuntimeExecTest.java:37:52:37:60 | "/bin/sh" | "/bin/sh" | RuntimeExecTest.java:17:25:17:51 | getenv(...) | getenv(...) : String | diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.qlref b/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.qlref new file mode 100644 index 00000000000..6916c533a16 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/CommandInjectionRuntimeExecLocal.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/RuntimeExecTest.java b/java/ql/test/experimental/query-tests/security/CWE-078/RuntimeExecTest.java new file mode 100644 index 00000000000..203c3855c87 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/RuntimeExecTest.java @@ -0,0 +1,47 @@ +/* Tests for command injection query + * + * This is suitable for testing static analysis tools, as long as they treat local input as an attack surface (which can be prone to false positives) + * + * (C) Copyright GitHub, 2023 + * + */ + +import java.util.stream.Stream; +import java.io.IOException; +import java.util.Arrays; + +public class RuntimeExecTest { + public static void test() { + System.out.println("Command injection test"); + + String script = System.getenv("SCRIPTNAME"); + + if (script != null) { + try { + // 1. array literal in the args + Runtime.getRuntime().exec(new String[]{"/bin/sh", script}); + + // 2. array literal with dataflow + String[] commandArray1 = new String[]{"/bin/sh", script}; + Runtime.getRuntime().exec(commandArray1); + + // 3. array assignment after it is created + String[] commandArray2 = new String[4]; + commandArray2[0] = "/bin/sh"; + commandArray2[1] = script; + Runtime.getRuntime().exec(commandArray2); + + // 4. Stream concatenation + Runtime.getRuntime().exec( + Stream.concat( + Arrays.stream(new String[]{"/bin/sh"}), + Arrays.stream(new String[]{script}) + ).toArray(String[]::new) + ); + + } catch (Exception e) { + System.err.println("ERROR: " + e.getMessage()); + } + } + } +} diff --git a/java/ql/test/library-tests/dataflow/field-value/A.java b/java/ql/test/library-tests/dataflow/field-value/A.java new file mode 100644 index 00000000000..d5e73b34634 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/field-value/A.java @@ -0,0 +1,25 @@ +import java.io.FilterInputStream; +import java.io.InputStream; + +public class A { + + public String src; + + private static void sink(Object o) {} + + public void test() { + sink(src); // $ hasTaintFlow + } + + class TestFis extends FilterInputStream { + + protected TestFis(InputStream in) { + super(in); + } + + public void testOutOfSource() { + // out of source field + sink(this.in); // $ hasTaintFlow + } + } +} diff --git a/java/ql/test/library-tests/dataflow/field-value/test.expected b/java/ql/test/library-tests/dataflow/field-value/test.expected new file mode 100644 index 00000000000..48de9172b36 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/field-value/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/field-value/test.ql b/java/ql/test/library-tests/dataflow/field-value/test.ql new file mode 100644 index 00000000000..4c364e8df70 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/field-value/test.ql @@ -0,0 +1,10 @@ +import java +import TestUtilities.InlineFlowTest + +module FieldValueConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof DataFlow::FieldValueNode } + + predicate isSink(DataFlow::Node sink) { DefaultFlowConfig::isSink(sink) } +} + +import TaintFlowTest diff --git a/java/ql/test/library-tests/dataflow/partial/test.expected b/java/ql/test/library-tests/dataflow/partial/test.expected index 0b3cebcb18b..2e04a437d9f 100644 --- a/java/ql/test/library-tests/dataflow/partial/test.expected +++ b/java/ql/test/library-tests/dataflow/partial/test.expected @@ -1,5 +1,6 @@ edges | A.java:4:16:4:18 | this [post update] [elem] | A.java:22:17:22:25 | new Box(...) [elem] | +| A.java:5:19:5:22 | elem | A.java:24:10:24:19 | other.elem | | A.java:12:5:12:5 | b [post update] : Box [elem] | A.java:13:12:13:12 | b : Box [elem] | | A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:5 | b [post update] : Box [elem] | | A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:18 | ...=... : Object | diff --git a/java/ql/test/library-tests/dataflow/partial/testRev.expected b/java/ql/test/library-tests/dataflow/partial/testRev.expected index bd44f641276..7c2ad279b7b 100644 --- a/java/ql/test/library-tests/dataflow/partial/testRev.expected +++ b/java/ql/test/library-tests/dataflow/partial/testRev.expected @@ -1,5 +1,6 @@ edges | A.java:4:16:4:18 | this [post update] [elem] | A.java:22:17:22:25 | new Box(...) [elem] | +| A.java:5:19:5:22 | elem | A.java:24:10:24:19 | other.elem | | A.java:12:5:12:5 | b [post update] : Box [elem] | A.java:13:12:13:12 | b : Box [elem] | | A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:5 | b [post update] : Box [elem] | | A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:18 | ...=... : Object | @@ -18,5 +19,6 @@ edges | 0 | A.java:23:13:23:17 | other [post update] [elem] | | 0 | A.java:24:10:24:14 | other [elem] | | 1 | A.java:4:16:4:18 | this [post update] [elem] | +| 1 | A.java:5:19:5:22 | elem | | 1 | A.java:28:5:28:5 | b [post update] [elem] | | 1 | A.java:28:14:28:25 | new Object(...) | diff --git a/java/ql/test/library-tests/dataflow/stream-read/A.java b/java/ql/test/library-tests/dataflow/stream-read/A.java new file mode 100644 index 00000000000..779f95bcefa --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/A.java @@ -0,0 +1,139 @@ +import java.io.InputStream; +import java.io.IOException; + +public class A { + + private static InputStream source() { + return null; + } + + private static void sink(Object s) {} + + static class MyStream extends InputStream { + private InputStream wrapped; + + MyStream(InputStream wrapped) { + this.wrapped = wrapped; + } + + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return wrapped.read(b); + } + } + + public static void testSeveralWrappers() { + InputStream src = source(); + InputStream wrapper1 = new MyStream(src); + sink(wrapper1); // $ hasTaintFlow + InputStream wrapper2 = new MyStream(wrapper1); + sink(wrapper2); // $ hasTaintFlow + InputStream wrapper3 = new MyStream(wrapper2); + sink(wrapper3); // $ hasTaintFlow + + InputStream wrapper4 = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return wrapper3.read(b); + + } + }; + sink(wrapper4); // $ hasTaintFlow + } + + public static void testAnonymous() throws Exception { + InputStream wrapper = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + InputStream in = source(); + return in.read(b); + } + }; + sink(wrapper); // $ hasTaintFlow + } + + public static void testAnonymousVarCapture() throws Exception { + InputStream in = source(); + InputStream wrapper = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + + } + }; + sink(wrapper); // $ hasTaintFlow + } + + public static InputStream wrapStream(InputStream in) { + return new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + } + }; + } + + public static void testWrapCall() { + sink(wrapStream(null)); // $ SPURIOUS: hasTaintFlow + sink(wrapStream(source())); // $ hasTaintFlow + } + + public static void testLocal() { + + class LocalInputStream extends InputStream { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + InputStream in = source(); + return in.read(b); + } + } + sink(new LocalInputStream()); // $ hasTaintFlow + } + + public static void testLocalVarCapture() { + InputStream in = source(); + + class LocalInputStream extends InputStream { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + } + } + sink(new LocalInputStream()); // $ hasTaintFlow + } +} diff --git a/java/ql/test/library-tests/dataflow/stream-read/test.expected b/java/ql/test/library-tests/dataflow/stream-read/test.expected new file mode 100644 index 00000000000..48de9172b36 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/stream-read/test.ql b/java/ql/test/library-tests/dataflow/stream-read/test.ql new file mode 100644 index 00000000000..50e3f8d2f7d --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/test.ql @@ -0,0 +1,2 @@ +import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/threat-models/Empty.java b/java/ql/test/library-tests/dataflow/threat-models/Empty.java new file mode 100644 index 00000000000..5be35d3d2db --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/Empty.java @@ -0,0 +1 @@ +class Empty { } diff --git a/java/ql/test/library-tests/dataflow/threat-models/threat-models1.expected b/java/ql/test/library-tests/dataflow/threat-models/threat-models1.expected new file mode 100644 index 00000000000..d538019ccb7 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/threat-models1.expected @@ -0,0 +1,5 @@ +| default | +| remote | +| request | +| response | +| uri-path | diff --git a/java/ql/test/library-tests/dataflow/threat-models/threat-models1.ql b/java/ql/test/library-tests/dataflow/threat-models/threat-models1.ql new file mode 100644 index 00000000000..11371a749dd --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/threat-models1.ql @@ -0,0 +1,5 @@ +import semmle.code.java.dataflow.ExternalFlowConfiguration as ExternalFlowConfiguration + +query predicate supportedThreatModels(string kind) { + ExternalFlowConfiguration::sourceModelKindConfig(kind) +} diff --git a/java/ql/test/library-tests/dataflow/threat-models/threat-models2.expected b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.expected new file mode 100644 index 00000000000..809a018e98e --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.expected @@ -0,0 +1,10 @@ +| cli | +| database | +| default | +| environment | +| file | +| local | +| remote | +| request | +| response | +| uri-path | diff --git a/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ext.yml b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ext.yml new file mode 100644 index 00000000000..1d6ed8c4992 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ext.yml @@ -0,0 +1,7 @@ +extensions: + + - addsTo: + pack: codeql/java-all + extensible: supportedThreatModels + data: + - ["local"] # Add the "local" group threat model. diff --git a/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ql b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ql new file mode 100644 index 00000000000..11371a749dd --- /dev/null +++ b/java/ql/test/library-tests/dataflow/threat-models/threat-models2.ql @@ -0,0 +1,5 @@ +import semmle.code.java.dataflow.ExternalFlowConfiguration as ExternalFlowConfiguration + +query predicate supportedThreatModels(string kind) { + ExternalFlowConfiguration::sourceModelKindConfig(kind) +} diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected new file mode 100644 index 00000000000..8b6f95795b0 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected @@ -0,0 +1,2 @@ +| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | +| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.qlref b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.qlref new file mode 100644 index 00000000000..b97c87be55f --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelApplicationModeExtractCandidates.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.expected b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.expected new file mode 100644 index 00000000000..0c626ca2d0b --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.expected @@ -0,0 +1,2 @@ +| Test.java:40:14:40:21 | openPath | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:40:4:40:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | +| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.qlref b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.qlref new file mode 100644 index 00000000000..43610d3cc1a --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractNegativeExamples.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected new file mode 100644 index 00000000000..841599ce021 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected @@ -0,0 +1,3 @@ +| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | +| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | +| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.qlref b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.qlref new file mode 100644 index 00000000000..585a78f94f9 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/Test.java b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/Test.java new file mode 100644 index 00000000000..67423855a8b --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/Test.java @@ -0,0 +1,50 @@ +package com.github.codeql.test; + +import java.io.InputStream; +import java.nio.file.CopyOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.io.File; + + +class Test { + public static void main(String[] args) throws Exception { + AtomicReference reference = new AtomicReference<>(); // uninteresting (parameterless constructor) + reference.set(args[0]); // arg[0] is not a candidate (modeled as value flow step) + // ^^^^^^ Argument[this] is a candidate + } + + public static void callSupplier(Supplier supplier) { + supplier.get(); // Argument[this] is a candidate + } + + public static void copyFiles(Path source, Path target, CopyOption option) throws Exception { + Files.copy( + source, // positive example (known sink) + target, // positive example (known sink) + option // no candidate (not modeled, but source and target are modeled) + ); + } + + public static InputStream getInputStream(Path openPath) throws Exception { + return Files.newInputStream( + openPath // positive example (known sink) + ); + } + + public static InputStream getInputStream(String openPath) throws Exception { + return Test.getInputStream( + Paths.get(openPath) // no candidate (argument to local call) + ); + } + + public static int compareFiles(File f1, File f2) { + return f1.compareTo( + f2 // negative example (modeled as not a sink) + ); + } +} + diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected new file mode 100644 index 00000000000..6fc82461869 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected @@ -0,0 +1,7 @@ +| com/github/codeql/test/PublicClass.java:4:15:4:19 | stuff | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:4:15:4:19 | stuff | MethodDoc | com/github/codeql/test/PublicClass.java:4:15:4:19 | stuff | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://this:1:1:1:1 | this | parameterName | +| com/github/codeql/test/PublicClass.java:4:21:4:30 | arg | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:4:21:4:30 | arg | MethodDoc | com/github/codeql/test/PublicClass.java:4:21:4:30 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://arg:1:1:1:1 | arg | parameterName | +| com/github/codeql/test/PublicClass.java:8:34:8:43 | arg | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:8:34:8:43 | arg | MethodDoc | com/github/codeql/test/PublicClass.java:8:34:8:43 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://false:1:1:1:1 | false | subtypes | file://staticStuff:1:1:1:1 | staticStuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://arg:1:1:1:1 | arg | parameterName | +| com/github/codeql/test/PublicInterface.java:4:17:4:21 | stuff | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:4:17:4:21 | stuff | MethodDoc | com/github/codeql/test/PublicInterface.java:4:17:4:21 | stuff | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://this:1:1:1:1 | this | parameterName | +| com/github/codeql/test/PublicInterface.java:4:23:4:32 | arg | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:4:23:4:32 | arg | MethodDoc | com/github/codeql/test/PublicInterface.java:4:23:4:32 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://arg:1:1:1:1 | arg | parameterName | +| com/github/codeql/test/PublicInterface.java:6:36:6:45 | arg | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:6:36:6:45 | arg | MethodDoc | com/github/codeql/test/PublicInterface.java:6:36:6:45 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://false:1:1:1:1 | false | subtypes | file://staticStuff:1:1:1:1 | staticStuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://arg:1:1:1:1 | arg | parameterName | +| java/nio/file/Files.java:10:9:10:24 | out | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | java/nio/file/Files.java:10:9:10:24 | out | MethodDoc | java/nio/file/Files.java:10:9:10:24 | out | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,OutputStream):1:1:1:1 | (Path,OutputStream) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://out:1:1:1:1 | out | parameterName | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.qlref b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.qlref new file mode 100644 index 00000000000..e68551eb3ec --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelFrameworkModeExtractCandidates.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected new file mode 100644 index 00000000000..6547f254401 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected @@ -0,0 +1,2 @@ +| java/io/File.java:4:9:4:17 | compareTo | known non-sink\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | java/io/File.java:4:9:4:17 | compareTo | MethodDoc | java/io/File.java:4:9:4:17 | compareTo | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://this:1:1:1:1 | this | parameterName | +| java/io/File.java:5:9:5:21 | pathname | known non-sink\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | java/io/File.java:5:9:5:21 | pathname | MethodDoc | java/io/File.java:5:9:5:21 | pathname | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://pathname:1:1:1:1 | pathname | parameterName | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.qlref b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.qlref new file mode 100644 index 00000000000..d58a0997fdc --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.expected b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.expected new file mode 100644 index 00000000000..b3326654de1 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.expected @@ -0,0 +1 @@ +| java/nio/file/Files.java:9:9:9:19 | source | path-injection\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | java/nio/file/Files.java:9:9:9:19 | source | MethodDoc | java/nio/file/Files.java:9:9:9:19 | source | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,OutputStream):1:1:1:1 | (Path,OutputStream) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://source:1:1:1:1 | source | parameterName | diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.qlref b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.qlref new file mode 100644 index 00000000000..ae52dc66496 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractPositiveExamples.qlref @@ -0,0 +1 @@ +Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java new file mode 100644 index 00000000000..b106d3da594 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/NonPublicClass.java @@ -0,0 +1,10 @@ +package com.github.codeql.test; + +/** + * No candidates in this class, as it's not public! + */ +class NonPublicClass { + public void noCandidates(String here) { + System.out.println(here); + } +} diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java new file mode 100644 index 00000000000..bf858b49163 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java @@ -0,0 +1,21 @@ +package com.github.codeql.test; + +public class PublicClass { + public void stuff(String arg) { // `arg` is a candidate, `this` is a candidate + System.out.println(arg); + } + + public static void staticStuff(String arg) { // `arg` is a candidate, `this` is not a candidate (static method) + System.out.println(arg); + } + + // `arg` and `this` are not a candidate because the method is not public: + protected void nonPublicStuff(String arg) { + System.out.println(arg); + } + + // `arg` and `this are not candidates because the method is not public: + void packagePrivateStuff(String arg) { + System.out.println(arg); + } +} diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java new file mode 100644 index 00000000000..e28e0559e0c --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicInterface.java @@ -0,0 +1,9 @@ +package com.github.codeql.test; + +public interface PublicInterface { + public void stuff(String arg); // `arg` is a candidate, `this` is a candidate + + public static void staticStuff(String arg) { // `arg` is a candidate, `this` is not a candidate (static method) + System.out.println(arg); + } +} diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/io/File.java b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/io/File.java new file mode 100644 index 00000000000..fa812d47efd --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/io/File.java @@ -0,0 +1,9 @@ +package java.io; + +public class File { + int compareTo( // `this` is a negative example - this is modeled as a neutral model + File pathname // negative example - this is modeled as a neutral model + ) { + return 0; + } +} diff --git a/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/nio/file/Files.java b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/nio/file/Files.java new file mode 100644 index 00000000000..76bfdfcf418 --- /dev/null +++ b/java/ql/test/query-tests/Telemetry/AutomodelFrameworkModeExtraction/java/nio/file/Files.java @@ -0,0 +1,19 @@ +package java.nio.file; + +import java.nio.file.Path; +import java.io.IOException; +import java.io.OutputStream; + +public class Files { + public static void copy( + Path source, // a positive example because a manual model exists + OutputStream out /* a candidate. NB: may be worthwhile to implement the + same behavior as in application mode where out would not be a + candidate because there already is a model for another parameter of + the same method and we assume that methods are always modeled + completely. + */ + ) throws IOException { + // ... + } +} diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index ed2b926666e..fcd73315764 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.7.2 + +### Minor Analysis Improvements + +* Added `log-injection` as a customizable sink kind for log injection. + ## 0.7.1 No user-facing changes. diff --git a/javascript/ql/lib/change-notes/released/0.7.2.md b/javascript/ql/lib/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..3d9ca6e6141 --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.7.2.md @@ -0,0 +1,5 @@ +## 0.7.2 + +### Minor Analysis Improvements + +* Added `log-injection` as a customizable sink kind for log injection. diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index f5bf5786071..16b02fb7423 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.7.1 +version: 0.7.2 groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index e4929e4eb39..096877900c9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -20,9 +20,7 @@ module Vue { private class VueExportEntryPoint extends API::EntryPoint { VueExportEntryPoint() { this = "VueExportEntryPoint" } - override DataFlow::Node getASink() { - result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport() - } + override DataFlow::Node getASink() { result = getModuleFromVueFile(_).getDefaultOrBulkExport() } } /** @@ -455,6 +453,13 @@ module Vue { } } + private Module getModuleFromVueFile(VueFile file) { + exists(HTML::ScriptElement elem | + xmlElements(elem, _, _, _, file) and // Avoid materializing all of Locatable.getFile() + result.getTopLevel() = elem.getScript() + ) + } + /** * A single file Vue component in a `.vue` file. */ @@ -482,12 +487,7 @@ module Vue { } /** Gets the module defined by the `script` tag in this .vue file, if any. */ - Module getModule() { - exists(HTML::ScriptElement elem | - xmlElements(elem, _, _, _, file) and // Avoid materializing all of Locatable.getFile() - result.getTopLevel() = elem.getScript() - ) - } + Module getModule() { result = getModuleFromVueFile(file) } override API::Node getComponentRef() { // There is no explicit `new Vue()` call in .vue files, so instead get all the imports diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 2e598711fcc..1cb4e189339 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -454,6 +454,14 @@ private API::Node getNodeFromPath(string type, AccessPath path, int n) { or // Apply a type step typeStep(getNodeFromPath(type, path, n), result) + or + // Apply a fuzzy step (without advancing 'n') + path.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromPath(type, path, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + path.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromPath(type, path, n - 1) } /** @@ -500,6 +508,14 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) // will themselves find by following type-steps. n > 0 and n < subPath.getNumToken() + or + // Apply a fuzzy step (without advancing 'n') + subPath.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromSubPath(base, subPath, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + subPath.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromSubPath(base, subPath, n - 1) } /** @@ -561,7 +577,7 @@ private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) */ bindingset[name] private predicate isValidTokenNameInIdentifyingAccessPath(string name) { - name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"] + name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar", "Fuzzy"] or Specific::isExtraValidTokenNameInIdentifyingAccessPath(name) } @@ -572,7 +588,7 @@ private predicate isValidTokenNameInIdentifyingAccessPath(string name) { */ bindingset[name] private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) { - name = "ReturnValue" + name = ["ReturnValue", "Fuzzy"] or Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index ee2fd74b5de..4c9c8e147eb 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -192,6 +192,43 @@ API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken toke result.asSink() = node.(DataFlow::CallNode).getReceiver() } +/** + * Holds if `name` is the name of a built-in method on Object, Array, or String. + */ +private predicate isCommonBuiltinMethodName(string name) { + exists(JS::ExternalInstanceMemberDecl member | + member.getBaseName() in ["Object", "Array", "String"] and + name = member.getName() + ) +} + +/** + * Holds if fuzzy evaluation should not traverse through `call`. + */ +private predicate blockFuzzyCall(DataFlow::CallNode call) { + isCommonBuiltinMethodName(call.getCalleeName()) +} + +pragma[inline] +API::Node getAFuzzySuccessor(API::Node node) { + result = node.getAMember() and + // Block traversal into calls to built-ins like .toString() and .substring() + // Since there is no API node representing the call itself, block flow into the callee node. + not exists(DataFlow::CallNode call | + node.asSource() = call.getCalleeNode() and + blockFuzzyCall(call) + ) + or + result = node.getAParameter() + or + result = node.getReturn() + or + result = node.getPromised() + or + // include 'this' parameters but not 'this' arguments + result = node.getReceiver() and result.asSource() instanceof DataFlow::ThisNode +} + /** * Holds if `invoke` matches the JS-specific call site filter in `token`. */ diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/LogInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/LogInjectionQuery.qll index cfae5b83409..6a98db71c72 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/LogInjectionQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/LogInjectionQuery.qll @@ -66,3 +66,7 @@ class HtmlSanitizer extends Sanitizer instanceof HtmlSanitizerCall { } class JsonStringifySanitizer extends Sanitizer { JsonStringifySanitizer() { this = any(JsonStringifyCall c).getOutput() } } + +private class SinkFromModel extends Sink { + SinkFromModel() { this = ModelOutput::getASinkNode("log-injection").asSink() } +} diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index 05fd164a7f1..5d62761a097 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.2 + +No user-facing changes. + ## 0.7.1 ### Minor Analysis Improvements diff --git a/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp b/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp index e46616ade47..0eac315b977 100644 --- a/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp +++ b/javascript/ql/src/Security/CWE-730/ServerCrash.qhelp @@ -42,15 +42,13 @@

    - The following server implementation checks if a client-provided - file path is valid and throws an exception if the check fails. It can - be seen that the exception is uncaught, and it is therefore reasonable to - expect the server to respond with an error response to client requests - that cause the check to fail. - - But since the exception is uncaught in the context of an - asynchronous callback invocation (fs.access(...)), the - entire server will terminate instead. + The following server code checks if a client-provided file path is valid + before saving data to that path. It would be reasonable to expect that the + server responds with an error in case the request contains an invalid + file path. However, the server instead throws an exception, which is + uncaught in the context of the asynchronous callback invocation + (fs.access(...)). This causes the entire server to + terminate abruptly.

    @@ -67,11 +65,9 @@

    - An alternative is to use an async and - await for the asynchronous behavior, since the server - will then print warning messages about uncaught exceptions instead of - terminating, unless the server was started with the commandline option - --unhandled-rejections=strict: + To simplify exception handling, it may be advisable to switch to + async/await syntax instead of using callbacks, which allows wrapping the + entire request handler in a try/catch block:

    diff --git a/javascript/ql/src/Security/CWE-730/examples/server-crash.BAD.js b/javascript/ql/src/Security/CWE-730/examples/server-crash.BAD.js index 9642ced4c1a..fdb992a3f62 100644 --- a/javascript/ql/src/Security/CWE-730/examples/server-crash.BAD.js +++ b/javascript/ql/src/Security/CWE-730/examples/server-crash.BAD.js @@ -7,10 +7,13 @@ function save(rootDir, path, content) { } // write content to disk } + express().post("/save", (req, res) => { - fs.exists(rootDir, (exists) => { - if (!exists) { - console.error(`Server setup is corrupted, ${rootDir} does not exist!`); + fs.access(rootDir, (err) => { + if (err) { + console.error( + `Server setup is corrupted, ${rootDir} cannot be accessed!` + ); res.status(500); res.end(); return; diff --git a/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-A.js b/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-A.js index b23071ea53c..926400c2982 100644 --- a/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-A.js +++ b/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-A.js @@ -1,9 +1,9 @@ // ... express().post("/save", (req, res) => { - fs.exists(rootDir, (exists) => { + fs.access(rootDir, (err) => { // ... try { - save(rootDir, req.query.path, req.body); // GOOD no uncaught exception + save(rootDir, req.query.path, req.body); // GOOD exception is caught below res.status(200); res.end(); } catch (e) { diff --git a/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-B.js b/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-B.js index 2a795ac001f..eb9ca74cdb1 100644 --- a/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-B.js +++ b/javascript/ql/src/Security/CWE-730/examples/server-crash.GOOD-B.js @@ -1,12 +1,12 @@ // ... express().post("/save", async (req, res) => { - if (!(await fs.promises.exists(rootDir))) { - console.error(`Server setup is corrupted, ${rootDir} does not exist!`); + try { + await fs.promises.access(rootDir); + save(rootDir, req.query.path, req.body); // GOOD exception is caught below + res.status(200); + res.end(); + } catch (e) { res.status(500); res.end(); - return; } - save(rootDir, req.query.path, req.body); // MAYBE BAD, depends on the commandline options - res.status(200); - res.end(); }); diff --git a/javascript/ql/src/change-notes/released/0.7.2.md b/javascript/ql/src/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8693d609ec7 --- /dev/null +++ b/javascript/ql/src/change-notes/released/0.7.2.md @@ -0,0 +1,3 @@ +## 0.7.2 + +No user-facing changes. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 9837128cf26..a521b4b0aef 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.7.1 +version: 0.7.2 groups: - javascript - queries diff --git a/javascript/ql/test/library-tests/frameworks/data/test.expected b/javascript/ql/test/library-tests/frameworks/data/test.expected index 39439f64629..28d7229789d 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.expected +++ b/javascript/ql/test/library-tests/frameworks/data/test.expected @@ -66,6 +66,14 @@ taintFlow | test.js:231:59:231:66 | source() | test.js:231:59:231:66 | source() | | test.js:232:59:232:66 | source() | test.js:232:59:232:66 | source() | | test.js:233:59:233:66 | source() | test.js:233:59:233:66 | source() | +| test.js:237:21:237:28 | source() | test.js:237:21:237:28 | source() | +| test.js:238:25:238:32 | source() | test.js:238:25:238:32 | source() | +| test.js:239:27:239:34 | source() | test.js:239:27:239:34 | source() | +| test.js:241:17:241:24 | source() | test.js:241:17:241:24 | source() | +| test.js:244:33:244:40 | source() | test.js:244:33:244:40 | source() | +| test.js:249:28:249:35 | source() | test.js:249:28:249:35 | source() | +| test.js:252:15:252:22 | source() | test.js:252:15:252:22 | source() | +| test.js:254:32:254:39 | source() | test.js:254:32:254:39 | source() | isSink | test.js:54:18:54:25 | source() | test-sink | | test.js:55:22:55:29 | source() | test-sink | @@ -136,6 +144,14 @@ isSink | test.js:231:59:231:66 | source() | test-sink | | test.js:232:59:232:66 | source() | test-sink | | test.js:233:59:233:66 | source() | test-sink | +| test.js:237:21:237:28 | source() | test-sink | +| test.js:238:25:238:32 | source() | test-sink | +| test.js:239:27:239:34 | source() | test-sink | +| test.js:241:17:241:24 | source() | test-sink | +| test.js:244:33:244:40 | source() | test-sink | +| test.js:249:28:249:35 | source() | test-sink | +| test.js:252:15:252:22 | source() | test-sink | +| test.js:254:32:254:39 | source() | test-sink | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/javascript/ql/test/library-tests/frameworks/data/test.js b/javascript/ql/test/library-tests/frameworks/data/test.js index d34940bd065..ac702b82a8c 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.js +++ b/javascript/ql/test/library-tests/frameworks/data/test.js @@ -232,3 +232,27 @@ function typeVars() { testlib.typevar.left.x.getThis().getThis().right.mySink(source()); // NOT OK testlib.typevar.left.x.right.getThis().getThis().mySink(source()); // NOT OK } + +function fuzzy() { + testlib.fuzzyCall(source()); // NOT OK + testlib.foo.fuzzyCall(source()); // NOT OK + testlib.foo().fuzzyCall(source()); // NOT OK + new testlib.Blah().foo.bar(async p => { + p.fuzzyCall(source()); // NOT OK + p.otherCall(source()); // OK + p.fuzzyCall().laterMethod(source()); // OK + (await p.promise).fuzzyCall(source()); // NOT OK + }); + + const wrapped = _.partial(testlib.foo, [123]); + wrapped().fuzzyCall(source()); // NOT OK [INCONSISTENCY] - API graphs do not currently propagate return values through partial invocation + wrapped(p => p.fuzzyCall(source())); // NOT OK + + const wrappedSink = _.partial(testlib.fuzzyCall); + wrappedSink(source()); // NOT OK + + _.partial(testlib.fuzzyCall, source()); // NOT OK + + fuzzyCall(source()); // OK - does not come from 'testlib' + require('blah').fuzzyCall(source()); // OK - does not come from 'testlib' +} diff --git a/javascript/ql/test/library-tests/frameworks/data/test.ql b/javascript/ql/test/library-tests/frameworks/data/test.ql index c5e3ce9ea8c..5ee8d0e3f9c 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.ql +++ b/javascript/ql/test/library-tests/frameworks/data/test.ql @@ -54,6 +54,7 @@ class Sinks extends ModelInput::SinkModelCsv { "testlib;Member[typevar].TypeVar[ABC].Member[mySink].Argument[0];test-sink", "testlib;Member[typevar].TypeVar[ABC].TypeVar[ABC].Member[mySink].Argument[1];test-sink", "testlib;Member[typevar].TypeVar[LeftRight].Member[mySink].Argument[0];test-sink", + "testlib;Fuzzy.Member[fuzzyCall].Argument[0];test-sink" ] } } diff --git a/misc/suite-helpers/CHANGELOG.md b/misc/suite-helpers/CHANGELOG.md index d1c4b5782a9..052852d7899 100644 --- a/misc/suite-helpers/CHANGELOG.md +++ b/misc/suite-helpers/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.2 + +No user-facing changes. + ## 0.6.1 No user-facing changes. diff --git a/misc/suite-helpers/change-notes/released/0.6.2.md b/misc/suite-helpers/change-notes/released/0.6.2.md new file mode 100644 index 00000000000..43f80640fc5 --- /dev/null +++ b/misc/suite-helpers/change-notes/released/0.6.2.md @@ -0,0 +1,3 @@ +## 0.6.2 + +No user-facing changes. diff --git a/misc/suite-helpers/codeql-pack.release.yml b/misc/suite-helpers/codeql-pack.release.yml index 80fb0899f64..5501a2a1cc5 100644 --- a/misc/suite-helpers/codeql-pack.release.yml +++ b/misc/suite-helpers/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.1 +lastReleaseVersion: 0.6.2 diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index 51344ba29b3..544a95a4dee 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,4 +1,4 @@ name: codeql/suite-helpers -version: 0.6.1 +version: 0.6.2 groups: shared warnOnImplicitThis: true diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 6c74cee16c1..09e3fb6aa19 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.2 + +No user-facing changes. + ## 0.10.1 ### New Features diff --git a/python/ql/lib/change-notes/released/0.10.2.md b/python/ql/lib/change-notes/released/0.10.2.md new file mode 100644 index 00000000000..7d66e65b13a --- /dev/null +++ b/python/ql/lib/change-notes/released/0.10.2.md @@ -0,0 +1,3 @@ +## 0.10.2 + +No user-facing changes. diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index af7510b3cd6..f0e1af76699 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.10.1 +lastReleaseVersion: 0.10.2 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index a259fb34fd2..0c3fb609a22 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/python-all -version: 0.10.1 +version: 0.10.2 groups: python dbscheme: semmlecode.python.dbscheme extractor: python library: true upgrades: upgrades dependencies: + codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} diff --git a/python/ql/lib/semmle/python/dataflow/new/DataFlow.qll b/python/ql/lib/semmle/python/dataflow/new/DataFlow.qll index 26c011bfe19..75725a42a9e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/DataFlow.qll +++ b/python/ql/lib/semmle/python/dataflow/new/DataFlow.qll @@ -22,6 +22,8 @@ private import python * global (inter-procedural) data flow analyses. */ module DataFlow { - import internal.DataFlow + private import internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import internal.DataFlowImpl1 } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 97802218f3d..1ea2e3d4c9d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -1561,7 +1561,8 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl } /** Gets a viable run-time target for the call `call`. */ -DataFlowCallable viableCallable(ExtractedDataFlowCall call) { +DataFlowCallable viableCallable(DataFlowCall call) { + call instanceof ExtractedDataFlowCall and result = call.getCallable() or // A call to a library callable with a flow summary diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 29561b0f0a6..92f0f17ce82 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index b0de9745816..1975ac9781f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index b0de9745816..1975ac9781f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index aff14e7b44d..41c9c4ec1be 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll index 8a7ecbcb565..cffdefe41ba 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll @@ -2,6 +2,7 @@ * Provides Python-specific definitions for use in the data flow library. */ +private import codeql.dataflow.DataFlow // we need to export `Unit` for the DataFlowImpl* files private import python as Python @@ -13,3 +14,12 @@ module Public { import DataFlowPublic import DataFlowUtil } + +module PythonDataFlow implements InputSig { + import Private + import Public + + predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1; + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index eaf65a4d503..a6b029a32ba 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -22,8 +22,8 @@ import DataFlowDispatch DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ -predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) { - p.isParameterOf(c, pos) +predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + p.(ParameterNodeImpl).isParameterOf(c, pos) } /** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */ @@ -608,7 +608,7 @@ predicate jumpStepNotSharedWithTypeTracker(Node nodeFrom, Node nodeTo) { * Holds if data can flow from `nodeFrom` to `nodeTo` via an assignment to * content `c`. */ -predicate storeStep(Node nodeFrom, Content c, Node nodeTo) { +predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) { listStoreStep(nodeFrom, c, nodeTo) or setStoreStep(nodeFrom, c, nodeTo) @@ -806,7 +806,7 @@ predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode n /** * Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`. */ -predicate readStep(Node nodeFrom, Content c, Node nodeTo) { +predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) { subscriptReadStep(nodeFrom, c, nodeTo) or iterableUnpackingReadStep(nodeFrom, c, nodeTo) @@ -881,7 +881,7 @@ predicate attributeReadStep(Node nodeFrom, AttributeContent c, AttrRead nodeTo) * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -predicate clearsContent(Node n, Content c) { +predicate clearsContent(Node n, ContentSet c) { matchClearStep(n, c) or attributeClearStep(n, c) @@ -933,8 +933,6 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { */ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() } -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 2e598711fcc..1cb4e189339 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -454,6 +454,14 @@ private API::Node getNodeFromPath(string type, AccessPath path, int n) { or // Apply a type step typeStep(getNodeFromPath(type, path, n), result) + or + // Apply a fuzzy step (without advancing 'n') + path.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromPath(type, path, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + path.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromPath(type, path, n - 1) } /** @@ -500,6 +508,14 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) // will themselves find by following type-steps. n > 0 and n < subPath.getNumToken() + or + // Apply a fuzzy step (without advancing 'n') + subPath.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromSubPath(base, subPath, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + subPath.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromSubPath(base, subPath, n - 1) } /** @@ -561,7 +577,7 @@ private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) */ bindingset[name] private predicate isValidTokenNameInIdentifyingAccessPath(string name) { - name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"] + name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar", "Fuzzy"] or Specific::isExtraValidTokenNameInIdentifyingAccessPath(name) } @@ -572,7 +588,7 @@ private predicate isValidTokenNameInIdentifyingAccessPath(string name) { */ bindingset[name] private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) { - name = "ReturnValue" + name = ["ReturnValue", "Fuzzy"] or Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name) } diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 38a1198193e..d0a5d1b9da5 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -108,6 +108,23 @@ API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) ) } +pragma[inline] +API::Node getAFuzzySuccessor(API::Node node) { + result = node.getAMember() + or + result = node.getParameter(_) + or + result = node.getKeywordParameter(_) + or + result = node.getReturn() + or + result = node.getASubscript() + or + result = node.getAwaited() + or + result = node.getASubclass() +} + /** * Holds if `invoke` matches the PY-specific call site filter in `token`. */ diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 0d2fc2b6968..e09c8527f53 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.2 + +No user-facing changes. + ## 0.8.1 ### Minor Analysis Improvements diff --git a/python/ql/src/change-notes/released/0.8.2.md b/python/ql/src/change-notes/released/0.8.2.md new file mode 100644 index 00000000000..11c1f6119a5 --- /dev/null +++ b/python/ql/src/change-notes/released/0.8.2.md @@ -0,0 +1,3 @@ +## 0.8.2 + +No user-facing changes. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index 2f693f95ba6..404110129dc 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.8.1 +lastReleaseVersion: 0.8.2 diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index f98131de0da..09b83118847 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.8.1 +version: 0.8.2 groups: - python - queries diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 68de6ecd878..dffe93790d0 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -11,6 +11,11 @@ taintFlow | test.py:83:50:83:60 | ControlFlowNode for getSource() | test.py:83:8:83:61 | ControlFlowNode for Attribute() | | test.py:86:49:86:59 | ControlFlowNode for getSource() | test.py:86:8:86:60 | ControlFlowNode for Attribute() | | test.py:87:56:87:66 | ControlFlowNode for getSource() | test.py:87:8:87:67 | ControlFlowNode for Attribute() | +| test.py:114:19:114:29 | ControlFlowNode for getSource() | test.py:114:19:114:29 | ControlFlowNode for getSource() | +| test.py:115:20:115:30 | ControlFlowNode for getSource() | test.py:115:20:115:30 | ControlFlowNode for getSource() | +| test.py:116:31:116:41 | ControlFlowNode for getSource() | test.py:116:31:116:41 | ControlFlowNode for getSource() | +| test.py:117:31:117:41 | ControlFlowNode for getSource() | test.py:117:31:117:41 | ControlFlowNode for getSource() | +| test.py:118:35:118:45 | ControlFlowNode for getSource() | test.py:118:35:118:45 | ControlFlowNode for getSource() | isSink | test.py:4:8:4:8 | ControlFlowNode for x | test-sink | | test.py:7:17:7:17 | ControlFlowNode for x | test-sink | @@ -50,6 +55,11 @@ isSink | test.py:91:21:91:23 | ControlFlowNode for one | test-sink | | test.py:91:30:91:32 | ControlFlowNode for two | test-sink | | test.py:98:6:98:9 | ControlFlowNode for baz2 | test-sink | +| test.py:114:19:114:29 | ControlFlowNode for getSource() | test-sink | +| test.py:115:20:115:30 | ControlFlowNode for getSource() | test-sink | +| test.py:116:31:116:41 | ControlFlowNode for getSource() | test-sink | +| test.py:117:31:117:41 | ControlFlowNode for getSource() | test-sink | +| test.py:118:35:118:45 | ControlFlowNode for getSource() | test-sink | isSource | test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source | | test.py:9:8:9:14 | ControlFlowNode for alias() | test-source | @@ -89,6 +99,12 @@ isSource | test.py:104:32:104:37 | ControlFlowNode for param2 | test-source | | test.py:107:24:107:28 | ControlFlowNode for name1 | test-source | | test.py:107:31:107:35 | ControlFlowNode for name2 | test-source | +| test.py:114:19:114:29 | ControlFlowNode for getSource() | test-source | +| test.py:115:20:115:30 | ControlFlowNode for getSource() | test-source | +| test.py:116:31:116:41 | ControlFlowNode for getSource() | test-source | +| test.py:117:31:117:41 | ControlFlowNode for getSource() | test-source | +| test.py:118:35:118:45 | ControlFlowNode for getSource() | test-source | +| test.py:119:20:119:30 | ControlFlowNode for getSource() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] | diff --git a/python/ql/test/library-tests/frameworks/data/test.py b/python/ql/test/library-tests/frameworks/data/test.py index ea1a6e0d4d4..ba08c0d6fb1 100644 --- a/python/ql/test/library-tests/frameworks/data/test.py +++ b/python/ql/test/library-tests/frameworks/data/test.py @@ -60,7 +60,7 @@ class SubClass (ArgPos.MyClass): def foo(self, arg, named=2, otherName=3): pass - def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5): + def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5): pass ArgPos.anyParam(arg1, arg2, name=namedThing) @@ -72,7 +72,7 @@ mySink(Steps.preserveTaint(getSource())) # FLOW mySink(Steps.preserveTaint("safe", getSource())) # NO FLOW Steps.taintIntoCallback( - getSource(), + getSource(), lambda x: mySink(x), # FLOW lambda y: mySink(y), # FLOW lambda z: mySink(z) # NO FLOW @@ -106,3 +106,14 @@ class OtherSubClass (ArgPos.MyClass): def anyNamed(self, name1, name2=2): # Parameter[any-named] matches all non-self named parameters pass + +import testlib as testlib +import testlib.nestedlib as testlib2 +import otherlib as otherlib + +testlib.fuzzyCall(getSource()) # NOT OK +testlib2.fuzzyCall(getSource()) # NOT OK +testlib.foo.bar.baz.fuzzyCall(getSource()) # NOT OK +testlib.foo().bar().fuzzyCall(getSource()) # NOT OK +testlib.foo(lambda x: x.fuzzyCall(getSource())) # NOT OK +otherlib.fuzzyCall(getSource()) # OK diff --git a/python/ql/test/library-tests/frameworks/data/test.ql b/python/ql/test/library-tests/frameworks/data/test.ql index 6c7639dc0c3..d9f270e6caf 100644 --- a/python/ql/test/library-tests/frameworks/data/test.ql +++ b/python/ql/test/library-tests/frameworks/data/test.ql @@ -51,6 +51,8 @@ class Sinks extends ModelInput::SinkModelCsv { // testing package syntax "foo1.bar;Member[baz1].Argument[any];test-sink", // "foo2;Member[bar].Member[baz2].Argument[any];test-sink", // + // testing fuzzy + "testlib;Fuzzy.Member[fuzzyCall].Argument[0];test-sink", // ] } } diff --git a/ql/Cargo.lock b/ql/Cargo.lock index 5ce63783ea8..d9ce2ba4767 100644 Binary files a/ql/Cargo.lock and b/ql/Cargo.lock differ diff --git a/ql/buramu/Cargo.toml b/ql/buramu/Cargo.toml index d6b3ea6d7a6..94a3345553b 100644 --- a/ql/buramu/Cargo.toml +++ b/ql/buramu/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" lazy_static = "1.4.0" chrono = "0.4.26" rayon = "1.7.0" -regex = "1.9.1" +regex = "1.9.3" diff --git a/ql/extractor/Cargo.toml b/ql/extractor/Cargo.toml index 438173b45cf..f082d00f6f0 100644 --- a/ql/extractor/Cargo.toml +++ b/ql/extractor/Cargo.toml @@ -16,5 +16,5 @@ clap = { version = "4.2", features = ["derive"] } tracing = "0.1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } rayon = "1.7.0" -regex = "1.9.1" +regex = "1.9.3" codeql-extractor = { path = "../../shared/tree-sitter-extractor" } diff --git a/ruby/ql/consistency-queries/CfgConsistency.ql b/ruby/ql/consistency-queries/CfgConsistency.ql index 1961bbf7b3a..d7d5b84a402 100644 --- a/ruby/ql/consistency-queries/CfgConsistency.ql +++ b/ruby/ql/consistency-queries/CfgConsistency.ql @@ -1,8 +1,8 @@ -import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::Consistency +import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::Consistency import codeql.ruby.AST import codeql.ruby.CFG import codeql.ruby.controlflow.internal.Completion -import codeql.ruby.controlflow.internal.ControlFlowGraphImpl +import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as CfgImpl /** * All `Expr` nodes are `PostOrderTree`s @@ -14,7 +14,7 @@ query predicate nonPostOrderExpr(Expr e, string cls) { not e instanceof Namespace and not e instanceof Toplevel and exists(AstNode last, Completion c | - last(e, last, c) and + CfgImpl::last(e, last, c) and last != e and c instanceof NormalCompletion ) diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index 47221ac14e3..0a5357f8df1 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.2 + +No user-facing changes. + ## 0.7.1 ### New Features diff --git a/ruby/ql/lib/change-notes/released/0.7.2.md b/ruby/ql/lib/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8693d609ec7 --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.7.2.md @@ -0,0 +1,3 @@ +## 0.7.2 + +No user-facing changes. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll index ed132cd2049..b7bafc8c31a 100644 --- a/ruby/ql/lib/codeql/ruby/Concepts.qll +++ b/ruby/ql/lib/codeql/ruby/Concepts.qll @@ -1184,3 +1184,69 @@ module TemplateRendering { abstract DataFlow::Node getTemplate(); } } + +/** + * A data-flow node that constructs a LDAP query. + * + * Often, it is worthy of an alert if an LDAP query is constructed such that + * executing it would be a security risk. + * + * If it is important that the query is executed, use `LdapExecution`. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `LdapConstruction::Range` instead. + */ +class LdapConstruction extends DataFlow::Node instanceof LdapConstruction::Range { + /** Gets the argument that specifies the query to be constructed. */ + DataFlow::Node getQuery() { result = super.getQuery() } +} + +/** Provides a class for modeling new LDAP query construction APIs. */ +module LdapConstruction { + /** + * A data-flow node that constructs a LDAP query. + * + * Often, it is worthy of an alert if an LDAP query is constructed such that + * executing it would be a security risk. + * + * If it is important that the query is executed, use `LdapExecution`. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `LdapConstruction` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument that specifies the query to be constructed. */ + abstract DataFlow::Node getQuery(); + } +} + +/** + * A data-flow node that executes LDAP queries. + * + * If the context of interest is such that merely constructing a LDAP query + * would be valuable to report, consider using `LdapConstruction`. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `LdapExecution::Range` instead. + */ +class LdapExecution extends DataFlow::Node instanceof LdapExecution::Range { + /** Gets the argument that specifies the query to be executed. */ + DataFlow::Node getQuery() { result = super.getQuery() } +} + +/** Provides a class for modeling new LDAP query execution APIs. */ +module LdapExecution { + /** + * A data-flow node that executes LDAP queries. + * + * If the context of interest is such that merely constructing a LDAP query + * would be valuable to report, consider using `LdapConstruction`. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `LdapExecution` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument that specifies the query to be executed. */ + abstract DataFlow::Node getQuery(); + } +} diff --git a/ruby/ql/lib/codeql/ruby/DataFlow.qll b/ruby/ql/lib/codeql/ruby/DataFlow.qll index 1fe8ce399bb..aa6e7e0cd59 100644 --- a/ruby/ql/lib/codeql/ruby/DataFlow.qll +++ b/ruby/ql/lib/codeql/ruby/DataFlow.qll @@ -10,6 +10,8 @@ import codeql.Locations * global (inter-procedural) data flow analyses. */ module DataFlow { - import codeql.ruby.dataflow.internal.DataFlow + private import codeql.ruby.dataflow.internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import codeql.ruby.dataflow.internal.DataFlowImpl1 } diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll index 2052592b431..30107e3a252 100644 --- a/ruby/ql/lib/codeql/ruby/Frameworks.qll +++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll @@ -36,3 +36,4 @@ private import codeql.ruby.frameworks.Mysql2 private import codeql.ruby.frameworks.Pg private import codeql.ruby.frameworks.Yaml private import codeql.ruby.frameworks.Sequel +private import codeql.ruby.frameworks.Ldap diff --git a/ruby/ql/lib/codeql/ruby/ast/Statement.qll b/ruby/ql/lib/codeql/ruby/ast/Statement.qll index feddd8fa3f0..24e18fe4c36 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Statement.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Statement.qll @@ -3,7 +3,7 @@ private import codeql.ruby.CFG private import internal.AST private import internal.TreeSitter private import internal.Variable -private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl +private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as CfgImpl /** * A statement. @@ -12,13 +12,13 @@ private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl */ class Stmt extends AstNode, TStmt { /** Gets a control-flow node for this statement, if any. */ - CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } + CfgNodes::AstCfgNode getAControlFlowNode() { result.getAstNode() = this } /** Gets a control-flow entry node for this statement, if any */ - AstNode getAControlFlowEntryNode() { result = getAControlFlowEntryNode(this) } + AstNode getAControlFlowEntryNode() { result = CfgImpl::getAControlFlowEntryNode(this) } /** Gets the control-flow scope of this statement, if any. */ - CfgScope getCfgScope() { result = getCfgScope(this) } + CfgScope getCfgScope() { result = CfgImpl::getCfgScope(this) } /** Gets the enclosing callable, if any. */ Callable getEnclosingCallable() { result = this.getCfgScope() } diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index d68a5582e17..c18474df099 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -214,7 +214,7 @@ private module Propagation { any(StringComponentCfgNode c | isString(c, result) or - result = c.getNode().(StringComponentImpl).getValue() + result = c.getAstNode().(StringComponentImpl).getValue() ) } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll index 30045054503..350f07a431a 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll @@ -4,7 +4,6 @@ private import codeql.ruby.AST private import codeql.ruby.ast.internal.AST private import codeql.ruby.ast.internal.TreeSitter private import codeql.ruby.controlflow.ControlFlowGraph -private import internal.ControlFlowGraphImpl private import CfgNodes private import SuccessorTypes @@ -390,7 +389,7 @@ private module JoinBlockPredecessors { private predicate idOf(Ruby::AstNode x, int y) = equivalenceRelation(id/2)(x, y) int getId(JoinBlockPredecessor jbp) { - idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getNode()), result) + idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getAstNode()), result) or idOf(toGeneratedInclSynth(jbp.(EntryBasicBlock).getScope()), result) } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 507b37b6a8f..1a8c6bcc607 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -6,62 +6,26 @@ private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.internal.Constant private import codeql.ruby.ast.internal.Literal private import ControlFlowGraph -private import internal.ControlFlowGraphImpl +private import internal.ControlFlowGraphImpl as CfgImpl private import internal.Splitting /** An entry node for a given scope. */ -class EntryNode extends CfgNode, TEntryNode { +class EntryNode extends CfgNode, CfgImpl::EntryNode { override string getAPrimaryQlClass() { result = "EntryNode" } - private CfgScope scope; - - EntryNode() { this = TEntryNode(scope) } - final override EntryBasicBlock getBasicBlock() { result = super.getBasicBlock() } - - final override Location getLocation() { result = scope.getLocation() } - - final override string toString() { result = "enter " + scope } } /** An exit node for a given scope, annotated with the type of exit. */ -class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode { +class AnnotatedExitNode extends CfgNode, CfgImpl::AnnotatedExitNode { override string getAPrimaryQlClass() { result = "AnnotatedExitNode" } - private CfgScope scope; - private boolean normal; - - AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) } - - /** Holds if this node represent a normal exit. */ - final predicate isNormal() { normal = true } - final override AnnotatedExitBasicBlock getBasicBlock() { result = super.getBasicBlock() } - - final override Location getLocation() { result = scope.getLocation() } - - final override string toString() { - exists(string s | - normal = true and s = "normal" - or - normal = false and s = "abnormal" - | - result = "exit " + scope + " (" + s + ")" - ) - } } /** An exit node for a given scope. */ -class ExitNode extends CfgNode, TExitNode { +class ExitNode extends CfgNode, CfgImpl::ExitNode { override string getAPrimaryQlClass() { result = "ExitNode" } - - private CfgScope scope; - - ExitNode() { this = TExitNode(scope) } - - final override Location getLocation() { result = scope.getLocation() } - - final override string toString() { result = "exit " + scope } } /** @@ -71,44 +35,18 @@ class ExitNode extends CfgNode, TExitNode { * (dead) code or not important for control flow, and multiple when there are different * splits for the AST node. */ -class AstCfgNode extends CfgNode, TElementNode { +class AstCfgNode extends CfgNode, CfgImpl::AstCfgNode { /** Gets the name of the primary QL class for this node. */ override string getAPrimaryQlClass() { result = "AstCfgNode" } - - private Splits splits; - AstNode e; - - AstCfgNode() { this = TElementNode(_, e, splits) } - - final override AstNode getNode() { result = e } - - override Location getLocation() { result = e.getLocation() } - - final override string toString() { - exists(string s | s = e.toString() | - result = "[" + this.getSplitsString() + "] " + s - or - not exists(this.getSplitsString()) and result = s - ) - } - - /** Gets a comma-separated list of strings for each split in this node, if any. */ - final string getSplitsString() { - result = splits.toString() and - result != "" - } - - /** Gets a split for this control flow node, if any. */ - final Split getASplit() { result = splits.getASplit() } } /** A control-flow node that wraps an AST expression. */ class ExprCfgNode extends AstCfgNode { override string getAPrimaryQlClass() { result = "ExprCfgNode" } - override Expr e; + Expr e; - ExprCfgNode() { e = this.getNode() } + ExprCfgNode() { e = this.getAstNode() } /** Gets the underlying expression. */ Expr getExpr() { result = e } @@ -123,12 +61,12 @@ class ReturningCfgNode extends AstCfgNode { ReturningStmt s; - ReturningCfgNode() { s = this.getNode() } + ReturningCfgNode() { s = this.getAstNode() } /** Gets the node of the returned value, if any. */ ExprCfgNode getReturnedValueNode() { result = this.getAPredecessor() and - result.getNode() = s.getValue() + result.getAstNode() = s.getValue() } } @@ -136,17 +74,19 @@ class ReturningCfgNode extends AstCfgNode { class StringComponentCfgNode extends AstCfgNode { override string getAPrimaryQlClass() { result = "StringComponentCfgNode" } - StringComponentCfgNode() { this.getNode() instanceof StringComponent } + StringComponentCfgNode() { this.getAstNode() instanceof StringComponent } /** Gets the constant value of this string component. */ - ConstantValue getConstantValue() { result = this.getNode().(StringComponent).getConstantValue() } + ConstantValue getConstantValue() { + result = this.getAstNode().(StringComponent).getConstantValue() + } } /** A control-flow node that wraps a `RegExpComponent` AST expression. */ class RegExpComponentCfgNode extends StringComponentCfgNode { override string getAPrimaryQlClass() { result = "RegExpComponentCfgNode" } - RegExpComponentCfgNode() { e instanceof RegExpComponent } + RegExpComponentCfgNode() { this.getAstNode() instanceof RegExpComponent } } private AstNode desugar(AstNode n) { @@ -179,7 +119,7 @@ abstract private class ChildMapping extends AstNode { cached predicate hasCfgChild(AstNode child, CfgNode cfn, CfgNode cfnChild) { this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and - cfnChild.getNode() = desugar(child) + cfnChild.getAstNode() = desugar(child) } } @@ -196,7 +136,7 @@ abstract private class ExprChildMapping extends Expr, ChildMapping { exists(BasicBlock mid | this.reachesBasicBlock(child, cfn, mid) and bb = mid.getAPredecessor() and - not mid.getANode().getNode() = child + not mid.getANode().getAstNode() = child ) } } @@ -210,13 +150,13 @@ abstract private class NonExprChildMapping extends ChildMapping { pragma[nomagic] override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) { this.relevantChild(child) and - cfn.getNode() = this and + cfn.getAstNode() = this and bb.getANode() = cfn or exists(BasicBlock mid | this.reachesBasicBlock(child, cfn, mid) and bb = mid.getASuccessor() and - not mid.getANode().getNode() = child + not mid.getANode().getAstNode() = child ) } } @@ -440,9 +380,11 @@ module ExprNodes { /** A control-flow node that wraps an `InClause` AST expression. */ class InClauseCfgNode extends AstCfgNode { - override string getAPrimaryQlClass() { result = "InClauseCfgNode" } + private InClauseChildMapping e; - override InClauseChildMapping e; + InClauseCfgNode() { e = this.getAstNode() } + + override string getAPrimaryQlClass() { result = "InClauseCfgNode" } /** Gets the pattern in this `in`-clause. */ final AstCfgNode getPattern() { e.hasCfgChild(e.getPattern(), this, result) } @@ -460,56 +402,60 @@ module ExprNodes { predicate patternReachesBasicBlock(int i, CfgNode cfnPattern, BasicBlock bb) { exists(Expr pattern | pattern = this.getPattern(i) and - cfnPattern.getNode() = pattern and + cfnPattern.getAstNode() = pattern and bb.getANode() = cfnPattern ) or exists(BasicBlock mid | this.patternReachesBasicBlock(i, cfnPattern, mid) and bb = mid.getASuccessor() and - not mid.getANode().getNode() = this + not mid.getANode().getAstNode() = this ) } predicate bodyReachesBasicBlock(CfgNode cfnBody, BasicBlock bb) { exists(Stmt body | body = this.getBody() and - cfnBody.getNode() = body and + cfnBody.getAstNode() = body and bb.getANode() = cfnBody ) or exists(BasicBlock mid | this.bodyReachesBasicBlock(cfnBody, mid) and bb = mid.getAPredecessor() and - not mid.getANode().getNode() = this + not mid.getANode().getAstNode() = this ) } } /** A control-flow node that wraps a `WhenClause` AST expression. */ class WhenClauseCfgNode extends AstCfgNode { - override string getAPrimaryQlClass() { result = "WhenClauseCfgNode" } + private WhenClauseChildMapping e; - override WhenClauseChildMapping e; + WhenClauseCfgNode() { e = this.getAstNode() } + + override string getAPrimaryQlClass() { result = "WhenClauseCfgNode" } /** Gets the body of this `when`-clause. */ final ExprCfgNode getBody() { - result.getNode() = desugar(e.getBody()) and + result.getAstNode() = desugar(e.getBody()) and e.bodyReachesBasicBlock(result, this.getBasicBlock()) } /** Gets the `i`th pattern this `when`-clause. */ final ExprCfgNode getPattern(int i) { - result.getNode() = desugar(e.getPattern(i)) and + result.getAstNode() = desugar(e.getPattern(i)) and e.patternReachesBasicBlock(i, result, this.getBasicBlock()) } } /** A control-flow node that wraps a `CasePattern`. */ class CasePatternCfgNode extends AstCfgNode { - override string getAPrimaryQlClass() { result = "CasePatternCfgNode" } + CasePattern e; - override CasePattern e; + CasePatternCfgNode() { e = this.getAstNode() } + + override string getAPrimaryQlClass() { result = "CasePatternCfgNode" } } private class ArrayPatternChildMapping extends NonExprChildMapping, ArrayPattern { @@ -871,7 +817,7 @@ module ExprNodes { /** A control-flow node that wraps an `InstanceVariableReadAccess` AST expression. */ class InstanceVariableReadAccessCfgNode extends InstanceVariableAccessCfgNode { - InstanceVariableReadAccessCfgNode() { this.getNode() instanceof InstanceVariableReadAccess } + InstanceVariableReadAccessCfgNode() { this.getAstNode() instanceof InstanceVariableReadAccess } override string getAPrimaryQlClass() { result = "InstanceVariableReadAccessCfgNode" } @@ -880,7 +826,9 @@ module ExprNodes { /** A control-flow node that wraps an `InstanceVariableWriteAccess` AST expression. */ class InstanceVariableWriteAccessCfgNode extends InstanceVariableAccessCfgNode { - InstanceVariableWriteAccessCfgNode() { this.getNode() instanceof InstanceVariableWriteAccess } + InstanceVariableWriteAccessCfgNode() { + this.getAstNode() instanceof InstanceVariableWriteAccess + } override string getAPrimaryQlClass() { result = "InstanceVariableWriteAccessCfgNode" } @@ -891,7 +839,9 @@ module ExprNodes { class StringInterpolationComponentCfgNode extends StringComponentCfgNode, StmtSequenceCfgNode { override string getAPrimaryQlClass() { result = "StringInterpolationComponentCfgNode" } - StringInterpolationComponentCfgNode() { this.getNode() instanceof StringInterpolationComponent } + StringInterpolationComponentCfgNode() { + this.getAstNode() instanceof StringInterpolationComponent + } final override ConstantValue getConstantValue() { result = StmtSequenceCfgNode.super.getConstantValue() @@ -902,7 +852,9 @@ module ExprNodes { class RegExpInterpolationComponentCfgNode extends RegExpComponentCfgNode, StmtSequenceCfgNode { override string getAPrimaryQlClass() { result = "RegExpInterpolationComponentCfgNode" } - RegExpInterpolationComponentCfgNode() { this.getNode() instanceof RegExpInterpolationComponent } + RegExpInterpolationComponentCfgNode() { + this.getAstNode() instanceof RegExpInterpolationComponent + } final override ConstantValue getConstantValue() { result = StmtSequenceCfgNode.super.getConstantValue() diff --git a/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll b/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll index 163cbc69dbd..9a386369e91 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll @@ -3,7 +3,7 @@ private import codeql.ruby.AST private import codeql.ruby.controlflow.BasicBlocks private import SuccessorTypes -private import internal.ControlFlowGraphImpl +private import internal.ControlFlowGraphImpl as CfgImpl private import internal.Splitting private import internal.Completion @@ -15,12 +15,12 @@ private import internal.Completion * Note that module declarations are not themselves CFG scopes, as they are part of * the CFG of the enclosing top-level or callable. */ -class CfgScope extends Scope instanceof CfgScopeImpl { +class CfgScope extends Scope instanceof CfgImpl::CfgScopeImpl { /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { exists(AstNode parent | parent = this.getParent() and - result = getCfgScope(parent) + result = CfgImpl::getCfgScope(parent) ) } } @@ -33,33 +33,18 @@ class CfgScope extends Scope instanceof CfgScopeImpl { * * Only nodes that can be reached from an entry point are included in the CFG. */ -class CfgNode extends TCfgNode { +class CfgNode extends CfgImpl::Node { /** Gets the name of the primary QL class for this node. */ string getAPrimaryQlClass() { none() } - /** Gets a textual representation of this control flow node. */ - string toString() { none() } - - /** Gets the AST node that this node corresponds to, if any. */ - AstNode getNode() { none() } - - /** Gets the location of this control flow node. */ - Location getLocation() { none() } - /** Gets the file of this control flow node. */ final File getFile() { result = this.getLocation().getFile() } - /** Holds if this control flow node has conditional successors. */ - final predicate isCondition() { exists(this.getASuccessor(any(ConditionalSuccessor bs))) } - - /** Gets the scope of this node. */ - final CfgScope getScope() { result = getNodeCfgScope(this) } - - /** Gets the basic block that this control flow node belongs to. */ - BasicBlock getBasicBlock() { result.getANode() = this } + /** DEPRECATED: Use `getAstNode` instead. */ + deprecated AstNode getNode() { result = this.getAstNode() } /** Gets a successor node of a given type, if any. */ - final CfgNode getASuccessor(SuccessorType t) { result = getASuccessor(this, t) } + final CfgNode getASuccessor(SuccessorType t) { result = super.getASuccessor(t) } /** Gets an immediate successor, if any. */ final CfgNode getASuccessor() { result = this.getASuccessor(_) } @@ -70,15 +55,12 @@ class CfgNode extends TCfgNode { /** Gets an immediate predecessor, if any. */ final CfgNode getAPredecessor() { result = this.getAPredecessor(_) } - /** Holds if this node has more than one predecessor. */ - final predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } - - /** Holds if this node has more than one successor. */ - final predicate isBranch() { strictcount(this.getASuccessor()) > 1 } + /** Gets the basic block that this control flow node belongs to. */ + BasicBlock getBasicBlock() { result.getANode() = this } } /** The type of a control flow successor. */ -class SuccessorType extends TSuccessorType { +class SuccessorType extends CfgImpl::TSuccessorType { /** Gets a textual representation of successor type. */ string toString() { none() } } @@ -86,7 +68,7 @@ class SuccessorType extends TSuccessorType { /** Provides different types of control flow successor types. */ module SuccessorTypes { /** A normal control flow successor. */ - class NormalSuccessor extends SuccessorType, TSuccessorSuccessor { + class NormalSuccessor extends SuccessorType, CfgImpl::TSuccessorSuccessor { final override string toString() { result = "successor" } } @@ -99,9 +81,9 @@ module SuccessorTypes { boolean value; ConditionalSuccessor() { - this = TBooleanSuccessor(value) or - this = TEmptinessSuccessor(value) or - this = TMatchingSuccessor(value) + this = CfgImpl::TBooleanSuccessor(value) or + this = CfgImpl::TEmptinessSuccessor(value) or + this = CfgImpl::TMatchingSuccessor(value) } /** Gets the Boolean value of this successor. */ @@ -125,7 +107,7 @@ module SuccessorTypes { * * `x >= 0` has both a `true` successor and a `false` successor. */ - class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor { } + class BooleanSuccessor extends ConditionalSuccessor, CfgImpl::TBooleanSuccessor { } /** * An emptiness control flow successor. @@ -158,7 +140,7 @@ module SuccessorTypes { * \___/ * ``` */ - class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor { + class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor { override string toString() { if value = true then result = "empty" else result = "non-empty" } } @@ -189,7 +171,7 @@ module SuccessorTypes { * puts "one" puts "not one" * ``` */ - class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor { + class MatchingSuccessor extends ConditionalSuccessor, CfgImpl::TMatchingSuccessor { override string toString() { if value = true then result = "match" else result = "no-match" } } @@ -207,7 +189,7 @@ module SuccessorTypes { * The exit node of `sum` is a `return` successor of the `return x + y` * statement. */ - class ReturnSuccessor extends SuccessorType, TReturnSuccessor { + class ReturnSuccessor extends SuccessorType, CfgImpl::TReturnSuccessor { final override string toString() { result = "return" } } @@ -230,7 +212,7 @@ module SuccessorTypes { * * The node `puts "done"` is `break` successor of the node `break`. */ - class BreakSuccessor extends SuccessorType, TBreakSuccessor { + class BreakSuccessor extends SuccessorType, CfgImpl::TBreakSuccessor { final override string toString() { result = "break" } } @@ -253,7 +235,7 @@ module SuccessorTypes { * * The node `x >= 0` is `next` successor of the node `next`. */ - class NextSuccessor extends SuccessorType, TNextSuccessor { + class NextSuccessor extends SuccessorType, CfgImpl::TNextSuccessor { final override string toString() { result = "next" } } @@ -278,7 +260,7 @@ module SuccessorTypes { * * The node `x -= 1` is `redo` successor of the node `redo`. */ - class RedoSuccessor extends SuccessorType, TRedoSuccessor { + class RedoSuccessor extends SuccessorType, CfgImpl::TRedoSuccessor { final override string toString() { result = "redo" } } @@ -302,7 +284,7 @@ module SuccessorTypes { * * The node `puts "Retry"` is `retry` successor of the node `retry`. */ - class RetrySuccessor extends SuccessorType, TRetrySuccessor { + class RetrySuccessor extends SuccessorType, CfgImpl::TRetrySuccessor { final override string toString() { result = "retry" } } @@ -323,7 +305,7 @@ module SuccessorTypes { * The exit node of `m` is an exceptional successor of the node * `raise "x > 2"`. */ - class RaiseSuccessor extends SuccessorType, TRaiseSuccessor { + class RaiseSuccessor extends SuccessorType, CfgImpl::TRaiseSuccessor { final override string toString() { result = "raise" } } @@ -344,7 +326,7 @@ module SuccessorTypes { * The exit node of `m` is an exit successor of the node * `exit 1`. */ - class ExitSuccessor extends SuccessorType, TExitSuccessor { + class ExitSuccessor extends SuccessorType, CfgImpl::TExitSuccessor { final override string toString() { result = "exit" } } } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll index dd7f124a69a..39fefcbeae1 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll @@ -8,7 +8,7 @@ private import codeql.ruby.AST private import codeql.ruby.ast.internal.AST private import codeql.ruby.ast.internal.Control private import codeql.ruby.controlflow.ControlFlowGraph -private import ControlFlowGraphImpl +private import ControlFlowGraphImpl as CfgImpl private import NonReturning private import SuccessorTypes @@ -53,7 +53,7 @@ private predicate nestedEnsureCompletion(TCompletion outer, int nestLevel) { or outer = TExitCompletion() ) and - nestLevel = any(Trees::BodyStmtTree t).getNestLevel() + nestLevel = any(CfgImpl::Trees::BodyStmtTree t).getNestLevel() } pragma[noinline] @@ -72,7 +72,7 @@ private predicate completionIsValidForStmt(AstNode n, Completion c) { } private AstNode getARescuableBodyChild() { - exists(Trees::BodyStmtTree bst | result = bst.getBodyChild(_, true) | + exists(CfgImpl::Trees::BodyStmtTree bst | result = bst.getBodyChild(_, true) | exists(bst.getARescue()) or exists(bst.getEnsure()) @@ -247,7 +247,7 @@ private predicate inMatchingContext(AstNode n) { or n = any(ReferencePattern p).getExpr() or - n.(Trees::DefaultValueParameterTree).hasDefaultValue() + n.(CfgImpl::Trees::DefaultValueParameterTree).hasDefaultValue() } /** diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll index 91effbf598f..c7a3b27afc8 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -1,45 +1,73 @@ /** - * Provides auxiliary classes and predicates used to construct the basic successor - * relation on control flow elements. - * - * The implementation is centered around the concept of a _completion_, which - * models how the execution of a statement or expression terminates. - * Completions are represented as an algebraic data type `Completion` defined in - * `Completion.qll`. - * - * The CFG is built by structural recursion over the AST. To achieve this the - * CFG edges related to a given AST node, `n`, are divided into three categories: - * - * 1. The in-going edge that points to the first CFG node to execute when - * `n` is going to be executed. - * 2. The out-going edges for control flow leaving `n` that are going to some - * other node in the surrounding context of `n`. - * 3. The edges that have both of their end-points entirely within the AST - * node and its children. - * - * The edges in (1) and (2) are inherently non-local and are therefore - * initially calculated as half-edges, that is, the single node, `k`, of the - * edge contained within `n`, by the predicates `k = first(n)` and `k = last(n, _)`, - * respectively. The edges in (3) can then be enumerated directly by the predicate - * `succ` by calling `first` and `last` recursively on the children of `n` and - * connecting the end-points. This yields the entire CFG, since all edges are in - * (3) for _some_ AST node. - * - * The second parameter of `last` is the completion, which is necessary to distinguish - * the out-going edges from `n`. Note that the completion changes as the calculation of - * `last` proceeds outward through the AST; for example, a `BreakCompletion` is - * caught up by its surrounding loop and turned into a `NormalCompletion`. + * Provides an implementation for constructing control-flow graphs (CFGs) from + * abstract syntax trees (ASTs), using the shared library from `codeql.controlflow.Cfg`. */ +private import codeql.controlflow.Cfg as CfgShared private import codeql.ruby.AST +private import codeql.ruby.AST as Ast private import codeql.ruby.ast.internal.AST as AstInternal private import codeql.ruby.ast.internal.Scope +private import codeql.ruby.ast.internal.Synthesis private import codeql.ruby.ast.Scope private import codeql.ruby.ast.internal.TreeSitter private import codeql.ruby.ast.internal.Variable private import codeql.ruby.controlflow.ControlFlowGraph private import Completion -import ControlFlowGraphImplShared + +class AstNode extends Ast::AstNode { + AstNode() { not any(Synthesis s).excludeFromControlFlowTree(this) } +} + +private module CfgInput implements CfgShared::InputSig { + private import ControlFlowGraphImpl as Impl + private import Completion as Comp + private import Splitting as Splitting + private import codeql.ruby.CFG as Cfg + + class AstNode = Impl::AstNode; + + class Completion = Comp::Completion; + + predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion } + + predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion } + + predicate completionIsValidFor(Completion c, AstNode e) { c.isValidFor(e) } + + class CfgScope = Cfg::CfgScope; + + CfgScope getCfgScope(AstNode n) { result = Impl::getCfgScope(n) } + + predicate scopeFirst(CfgScope scope, AstNode first) { scope.(Impl::CfgScopeImpl).entry(first) } + + predicate scopeLast(CfgScope scope, AstNode last, Completion c) { + scope.(Impl::CfgScopeImpl).exit(last, c) + } + + class SplitKindBase = Splitting::TSplitKind; + + class Split = Splitting::Split; + + class SuccessorType = Cfg::SuccessorType; + + SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } + + predicate successorTypeIsSimple(SuccessorType t) { + t instanceof Cfg::SuccessorTypes::NormalSuccessor + } + + predicate successorTypeIsCondition(SuccessorType t) { + t instanceof Cfg::SuccessorTypes::ConditionalSuccessor + } + + predicate isAbnormalExitType(SuccessorType t) { + t instanceof Cfg::SuccessorTypes::RaiseSuccessor or + t instanceof Cfg::SuccessorTypes::ExitSuccessor + } +} + +import CfgShared::Make abstract class CfgScopeImpl extends AstNode { abstract predicate entry(AstNode first); @@ -97,34 +125,34 @@ predicate succExit(CfgScopeImpl scope, AstNode last, Completion c) { scope.exit( /** Defines the CFG by dispatch on the various AST types. */ module Trees { - private class AliasStmtTree extends StandardPreOrderTree, AliasStmt { - final override ControlFlowTree getChildElement(int i) { - result = this.getNewName() and i = 0 + private class AliasStmtTree extends StandardPreOrderTree instanceof AliasStmt { + final override ControlFlowTree getChildNode(int i) { + result = super.getNewName() and i = 0 or - result = this.getOldName() and i = 1 + result = super.getOldName() and i = 1 } } - private class ArgumentListTree extends StandardPostOrderTree, ArgumentList { - final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) } + private class ArgumentListTree extends StandardPostOrderTree instanceof ArgumentList { + final override ControlFlowTree getChildNode(int i) { result = super.getElement(i) } } - private class AssignExprTree extends StandardPostOrderTree, AssignExpr { + private class AssignExprTree extends StandardPostOrderTree instanceof AssignExpr { AssignExprTree() { - exists(Expr left | left = this.getLeftOperand() | + exists(Expr left | left = super.getLeftOperand() | left instanceof VariableAccess or left instanceof ConstantAccess ) } - final override ControlFlowTree getChildElement(int i) { - result = this.getLeftOperand() and i = 0 + final override ControlFlowTree getChildNode(int i) { + result = super.getLeftOperand() and i = 0 or - result = this.getRightOperand() and i = 1 + result = super.getRightOperand() and i = 1 } } - private class BeginTree extends BodyStmtTree, BeginExpr { + private class BeginTree extends BodyStmtTree instanceof BeginExpr { final override predicate first(AstNode first) { this.firstInner(first) } final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } @@ -132,35 +160,43 @@ module Trees { final override predicate propagatesAbnormal(AstNode child) { none() } } - private class BlockArgumentTree extends StandardPostOrderTree, BlockArgument { - final override ControlFlowTree getChildElement(int i) { result = this.getValue() and i = 0 } + private class BlockArgumentTree extends StandardPostOrderTree instanceof BlockArgument { + final override ControlFlowTree getChildNode(int i) { result = super.getValue() and i = 0 } } - abstract private class NonDefaultValueParameterTree extends ControlFlowTree, NamedParameter { + abstract private class NonDefaultValueParameterTree extends ControlFlowTree instanceof NamedParameter + { final override predicate first(AstNode first) { - this.getDefiningAccess().(ControlFlowTree).first(first) + super.getDefiningAccess().(ControlFlowTree).first(first) or - not exists(this.getDefiningAccess()) and first = this + not exists(super.getDefiningAccess()) and first = this } final override predicate last(AstNode last, Completion c) { - this.getDefiningAccess().(ControlFlowTree).last(last, c) + super.getDefiningAccess().(ControlFlowTree).last(last, c) or - not exists(this.getDefiningAccess()) and + not exists(super.getDefiningAccess()) and last = this and c.isValidFor(this) } override predicate propagatesAbnormal(AstNode child) { - this.getDefiningAccess().(ControlFlowTree).propagatesAbnormal(child) + super.getDefiningAccess().(ControlFlowTree).propagatesAbnormal(child) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } } - private class BlockParameterTree extends NonDefaultValueParameterTree, BlockParameter { } + private class BlockParameterTree extends NonDefaultValueParameterTree instanceof BlockParameter { + } + + abstract class BodyStmtTree extends StmtSequenceTree instanceof BodyStmt { + /** Gets a rescue clause in this block. */ + final RescueClause getARescue() { result = super.getRescue(_) } + + /** Gets the `ensure` clause in this block, if any. */ + final StmtSequence getEnsure() { result = super.getEnsure() } - abstract class BodyStmtTree extends StmtSequenceTree, BodyStmt { override predicate first(AstNode first) { first = this } predicate firstInner(AstNode first) { @@ -168,16 +204,16 @@ module Trees { or not exists(this.getBodyChild(_, _)) and ( - first(this.getRescue(_), first) + first(super.getRescue(_), first) or - not exists(this.getRescue(_)) and - first(this.getEnsure(), first) + not exists(super.getRescue(_)) and + first(super.getEnsure(), first) ) } predicate lastInner(AstNode last, Completion c) { exists(boolean ensurable | last = this.getAnEnsurePredecessor(c, ensurable) | - not this.hasEnsure() + not super.hasEnsure() or ensurable = false ) @@ -194,10 +230,10 @@ module Trees { ) or not exists(this.getBodyChild(_, _)) and - not exists(this.getRescue(_)) and + not exists(super.getRescue(_)) and this.lastEnsure0(last, c) or - last([this.getEnsure(), this.getBodyChild(_, false)], last, c) and + last([super.getEnsure().(AstNode), this.getBodyChild(_, false)], last, c) and not c instanceof NormalCompletion } @@ -211,23 +247,23 @@ module Trees { or // Exceptional flow from body to first `rescue` this.lastBody(pred, c, true) and - first(this.getRescue(0), succ) and + first(super.getRescue(0), succ) and c instanceof RaiseCompletion or // Flow from one `rescue` clause to the next when there is no match - exists(RescueTree rescue, int i | rescue = this.getRescue(i) | + exists(RescueTree rescue, int i | rescue = super.getRescue(i) | rescue.lastNoMatch(pred, c) and - first(this.getRescue(i + 1), succ) + first(super.getRescue(i + 1), succ) ) or // Flow from body to `else` block when no exception this.lastBody(pred, c, _) and - first(this.getElse(), succ) and + first(super.getElse(), succ) and c instanceof NormalCompletion or // Flow into `ensure` block pred = this.getAnEnsurePredecessor(c, true) and - first(this.getEnsure(), succ) + first(super.getEnsure(), succ) } /** @@ -242,22 +278,22 @@ module Trees { // Any non-throw completion will always continue directly to the `ensure` block, // unless there is an `else` block not c instanceof RaiseCompletion and - not exists(this.getElse()) + not exists(super.getElse()) or // Any completion will continue to the `ensure` block when there are no `rescue` // blocks - not exists(this.getRescue(_)) + not exists(super.getRescue(_)) ) or // Last element from any matching `rescue` block continues to the `ensure` block - last(this.getRescue(_), result, c) and + last(super.getRescue(_), result, c) and ensurable = true or // If the last `rescue` block does not match, continue to the `ensure` block exists(int lst, MatchingCompletion mc | - this.getRescue(lst).(RescueTree).lastNoMatch(result, mc) and + super.getRescue(lst).(RescueTree).lastNoMatch(result, mc) and mc.getValue() = false and - not exists(this.getRescue(lst + 1)) and + not exists(super.getRescue(lst + 1)) and c = any(NestedEnsureCompletion nec | nec.getOuterCompletion() instanceof RaiseCompletion and @@ -268,12 +304,12 @@ module Trees { ) or // Last element of `else` block continues to the `ensure` block - last(this.getElse(), result, c) and + last(super.getElse(), result, c) and ensurable = true } pragma[nomagic] - private predicate lastEnsure0(AstNode last, Completion c) { last(this.getEnsure(), last, c) } + private predicate lastEnsure0(AstNode last, Completion c) { last(super.getEnsure(), last, c) } /** * Gets a descendant that belongs to the `ensure` block of this block, if any. @@ -281,7 +317,7 @@ module Trees { */ pragma[nomagic] AstNode getAnEnsureDescendant() { - result = this.getEnsure() + result = super.getEnsure() or exists(AstNode mid | mid = this.getAnEnsureDescendant() and @@ -302,7 +338,7 @@ module Trees { exists(StmtSequence innerEnsure | innerEnsure = this.getAnEnsureDescendant().getAChild() and getCfgScope(innerEnsure) = getCfgScope(this) and - innerEnsure = innerBlock.(BodyStmt).getEnsure() + innerEnsure = innerBlock.getEnsure() ) } @@ -343,19 +379,19 @@ module Trees { } } - private class BooleanLiteralTree extends LeafTree, BooleanLiteral { } + private class BooleanLiteralTree extends LeafTree instanceof BooleanLiteral { } - class BraceBlockTree extends StmtSequenceTree, BraceBlock { + class BraceBlockTree extends StmtSequenceTree instanceof BraceBlock { final override predicate propagatesAbnormal(AstNode child) { none() } final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getParameter(i) and rescuable = false + result = super.getParameter(i) and rescuable = false or - result = this.getLocalVariable(i - this.getNumberOfParameters()) and rescuable = false + result = super.getLocalVariable(i - super.getNumberOfParameters()) and rescuable = false or result = StmtSequenceTree.super - .getBodyChild(i - this.getNumberOfParameters() - count(this.getALocalVariable()), + .getBodyChild(i - super.getNumberOfParameters() - count(super.getALocalVariable()), rescuable) } @@ -371,7 +407,7 @@ module Trees { } } - private class CallTree extends StandardPostOrderTree, Call { + private class CallTree extends StandardPostOrderTree instanceof Call { CallTree() { // Logical operations are handled separately not this instanceof UnaryLogicalOperation and @@ -380,28 +416,28 @@ module Trees { not this.(MethodCall).isSafeNavigation() } - override ControlFlowTree getChildElement(int i) { result = this.getArgument(i) } + override ControlFlowTree getChildNode(int i) { result = super.getArgument(i) } } - private class CaseTree extends PostOrderTree, CaseExpr, AstInternal::TCaseExpr { + private class CaseTree extends PostOrderTree instanceof CaseExpr, AstInternal::TCaseExpr { final override predicate propagatesAbnormal(AstNode child) { - child = this.getValue() or child = this.getABranch() + child = super.getValue() or child = super.getABranch() } final override predicate first(AstNode first) { - first(this.getValue(), first) + first(super.getValue(), first) or - not exists(this.getValue()) and first(this.getBranch(0), first) + not exists(super.getValue()) and first(super.getBranch(0), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getValue(), pred, c) and - first(this.getBranch(0), succ) and + last(super.getValue(), pred, c) and + first(super.getBranch(0), succ) and c instanceof SimpleCompletion or - exists(int i, WhenTree branch | branch = this.getBranch(i) | + exists(int i, WhenTree branch | branch = super.getBranch(i) | pred = branch and - first(this.getBranch(i + 1), succ) and + first(super.getBranch(i + 1), succ) and c.isValidFor(branch) and c.(ConditionalCompletion).getValue() = false ) @@ -409,32 +445,32 @@ module Trees { succ = this and c instanceof NormalCompletion and ( - last(this.getValue(), pred, c) and not exists(this.getABranch()) + last(super.getValue(), pred, c) and not exists(super.getABranch()) or - last(this.getABranch().(WhenClause).getBody(), pred, c) + last(super.getABranch().(WhenClause).getBody(), pred, c) or exists(int i, ControlFlowTree lastBranch | - lastBranch = this.getBranch(i) and - not exists(this.getBranch(i + 1)) and + lastBranch = super.getBranch(i) and + not exists(super.getBranch(i + 1)) and last(lastBranch, pred, c) ) ) } } - private class CaseMatchTree extends PostOrderTree, CaseExpr, AstInternal::TCaseMatch { + private class CaseMatchTree extends PostOrderTree instanceof CaseExpr, AstInternal::TCaseMatch { final override predicate propagatesAbnormal(AstNode child) { - child = this.getValue() or child = this.getABranch() + child = super.getValue() or child = super.getABranch() } - final override predicate first(AstNode first) { first(this.getValue(), first) } + final override predicate first(AstNode first) { first(super.getValue(), first) } final override predicate last(AstNode last, Completion c) { super.last(last, c) or - not exists(this.getElseBranch()) and + not exists(super.getElseBranch()) and exists(MatchingCompletion lc, AstNode lastBranch | - lastBranch = max(int i | | this.getBranch(i) order by i) and + lastBranch = max(int i | | super.getBranch(i) order by i) and lc.getValue() = false and last(lastBranch, last, lc) and c instanceof RaiseCompletion and @@ -443,28 +479,28 @@ module Trees { } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getValue(), pred, c) and - first(this.getBranch(0), succ) and + last(super.getValue(), pred, c) and + first(super.getBranch(0), succ) and c instanceof SimpleCompletion or - exists(int i, AstNode branch | branch = this.getBranch(i) | + exists(int i, AstNode branch | branch = super.getBranch(i) | last(branch, pred, c) and - first(this.getBranch(i + 1), succ) and + first(super.getBranch(i + 1), succ) and c.(MatchingCompletion).getValue() = false ) or succ = this and c instanceof NormalCompletion and ( - last(this.getABranch(), pred, c) and + last(super.getABranch(), pred, c) and not c.(MatchingCompletion).getValue() = false or - last(this.getElseBranch(), pred, c) + last(super.getElseBranch(), pred, c) ) } } - private class PatternVariableAccessTree extends LocalVariableAccessTree, LocalVariableWriteAccess, + private class PatternVariableAccessTree extends LocalVariableAccessTree instanceof LocalVariableWriteAccess, CasePattern { final override predicate last(AstNode last, Completion c) { @@ -473,18 +509,18 @@ module Trees { } } - private class ArrayPatternTree extends ControlFlowTree, ArrayPattern { + private class ArrayPatternTree extends ControlFlowTree instanceof ArrayPattern { final override predicate propagatesAbnormal(AstNode child) { - child = this.getClass() or - child = this.getPrefixElement(_) or - child = this.getRestVariableAccess() or - child = this.getSuffixElement(_) + child = super.getClass() or + child = super.getPrefixElement(_) or + child = super.getRestVariableAccess() or + child = super.getSuffixElement(_) } final override predicate first(AstNode first) { - first(this.getClass(), first) + first(super.getClass(), first) or - not exists(this.getClass()) and first = this + not exists(super.getClass()) and first = this } final override predicate last(AstNode last, Completion c) { @@ -494,9 +530,9 @@ module Trees { c.isValidFor(this) or exists(AstNode node | - node = this.getClass() or - node = this.getPrefixElement(_) or - node = this.getSuffixElement(_) + node = super.getClass() or + node = super.getPrefixElement(_) or + node = super.getSuffixElement(_) | last(node, last, c) ) @@ -505,22 +541,22 @@ module Trees { c.(MatchingCompletion).getValue() = true and last = this and c.isValidFor(this) and - not exists(this.getPrefixElement(_)) and - not exists(this.getRestVariableAccess()) + not exists(super.getPrefixElement(_)) and + not exists(super.getRestVariableAccess()) or c.(MatchingCompletion).getValue() = true and - last(max(int i | | this.getPrefixElement(i) order by i), last, c) and - not exists(this.getRestVariableAccess()) + last(max(int i | | super.getPrefixElement(i) order by i), last, c) and + not exists(super.getRestVariableAccess()) or - last(this.getRestVariableAccess(), last, c) and - not exists(this.getSuffixElement(_)) + last(super.getRestVariableAccess(), last, c) and + not exists(super.getSuffixElement(_)) or c.(MatchingCompletion).getValue() = true and - last(max(int i | | this.getSuffixElement(i) order by i), last, c) + last(max(int i | | super.getSuffixElement(i) order by i), last, c) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getClass(), pred, c) and + last(super.getClass(), pred, c) and succ = this and c.(MatchingCompletion).getValue() = true or @@ -529,67 +565,67 @@ module Trees { c.(MatchingCompletion).getValue() = true and first(next, succ) | - next = this.getPrefixElement(0) + next = super.getPrefixElement(0) or - not exists(this.getPrefixElement(_)) and - next = this.getRestVariableAccess() + not exists(super.getPrefixElement(_)) and + next = super.getRestVariableAccess() ) or - last(max(int i | | this.getPrefixElement(i) order by i), pred, c) and - first(this.getRestVariableAccess(), succ) and + last(max(int i | | super.getPrefixElement(i) order by i), pred, c) and + first(super.getRestVariableAccess(), succ) and c.(MatchingCompletion).getValue() = true or exists(int i | - last(this.getPrefixElement(i), pred, c) and - first(this.getPrefixElement(i + 1), succ) and + last(super.getPrefixElement(i), pred, c) and + first(super.getPrefixElement(i + 1), succ) and c.(MatchingCompletion).getValue() = true ) or - last(this.getRestVariableAccess(), pred, c) and - first(this.getSuffixElement(0), succ) and + last(super.getRestVariableAccess(), pred, c) and + first(super.getSuffixElement(0), succ) and c instanceof SimpleCompletion or exists(int i | - last(this.getSuffixElement(i), pred, c) and - first(this.getSuffixElement(i + 1), succ) and + last(super.getSuffixElement(i), pred, c) and + first(super.getSuffixElement(i + 1), succ) and c.(MatchingCompletion).getValue() = true ) } } - private class FindPatternTree extends ControlFlowTree, FindPattern { + private class FindPatternTree extends ControlFlowTree instanceof FindPattern { final override predicate propagatesAbnormal(AstNode child) { - child = this.getClass() or - child = this.getPrefixVariableAccess() or - child = this.getElement(_) or - child = this.getSuffixVariableAccess() + child = super.getClass() or + child = super.getPrefixVariableAccess() or + child = super.getElement(_) or + child = super.getSuffixVariableAccess() } final override predicate first(AstNode first) { - first(this.getClass(), first) + first(super.getClass(), first) or - not exists(this.getClass()) and first = this + not exists(super.getClass()) and first = this } final override predicate last(AstNode last, Completion c) { - last(this.getSuffixVariableAccess(), last, c) + last(super.getSuffixVariableAccess(), last, c) or - last(max(int i | | this.getElement(i) order by i), last, c) and - not exists(this.getSuffixVariableAccess()) + last(max(int i | | super.getElement(i) order by i), last, c) and + not exists(super.getSuffixVariableAccess()) or c.(MatchingCompletion).getValue() = false and ( last = this and c.isValidFor(this) or - exists(AstNode node | node = this.getClass() or node = this.getElement(_) | + exists(AstNode node | node = super.getClass() or node = super.getElement(_) | last(node, last, c) ) ) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getClass(), pred, c) and + last(super.getClass(), pred, c) and succ = this and c.(MatchingCompletion).getValue() = true or @@ -598,39 +634,39 @@ module Trees { c.(MatchingCompletion).getValue() = true and first(next, succ) | - next = this.getPrefixVariableAccess() + next = super.getPrefixVariableAccess() or - not exists(this.getPrefixVariableAccess()) and - next = this.getElement(0) + not exists(super.getPrefixVariableAccess()) and + next = super.getElement(0) ) or - last(this.getPrefixVariableAccess(), pred, c) and - first(this.getElement(0), succ) and + last(super.getPrefixVariableAccess(), pred, c) and + first(super.getElement(0), succ) and c instanceof SimpleCompletion or c.(MatchingCompletion).getValue() = true and exists(int i | - last(this.getElement(i), pred, c) and - first(this.getElement(i + 1), succ) + last(super.getElement(i), pred, c) and + first(super.getElement(i + 1), succ) ) or c.(MatchingCompletion).getValue() = true and - last(max(int i | | this.getElement(i) order by i), pred, c) and - first(this.getSuffixVariableAccess(), succ) + last(max(int i | | super.getElement(i) order by i), pred, c) and + first(super.getSuffixVariableAccess(), succ) } } - private class HashPatternTree extends ControlFlowTree, HashPattern { + private class HashPatternTree extends ControlFlowTree instanceof HashPattern { final override predicate propagatesAbnormal(AstNode child) { - child = this.getClass() or - child = this.getValue(_) or - child = this.getRestVariableAccess() + child = super.getClass() or + child = super.getValue(_) or + child = super.getRestVariableAccess() } final override predicate first(AstNode first) { - first(this.getClass(), first) + first(super.getClass(), first) or - not exists(this.getClass()) and first = this + not exists(super.getClass()) and first = this } final override predicate last(AstNode last, Completion c) { @@ -640,8 +676,8 @@ module Trees { c.isValidFor(this) or exists(AstNode node | - node = this.getClass() or - node = this.getValue(_) + node = super.getClass() or + node = super.getValue(_) | last(node, last, c) ) @@ -649,18 +685,18 @@ module Trees { or c.(MatchingCompletion).getValue() = true and last = this and - not exists(this.getValue(_)) and - not exists(this.getRestVariableAccess()) + not exists(super.getValue(_)) and + not exists(super.getRestVariableAccess()) or c.(MatchingCompletion).getValue() = true and - last(max(int i | | this.getValue(i) order by i), last, c) and - not exists(this.getRestVariableAccess()) + last(max(int i | | super.getValue(i) order by i), last, c) and + not exists(super.getRestVariableAccess()) or - last(this.getRestVariableAccess(), last, c) + last(super.getRestVariableAccess(), last, c) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getClass(), pred, c) and + last(super.getClass(), pred, c) and succ = this and c.(MatchingCompletion).getValue() = true or @@ -669,102 +705,103 @@ module Trees { c.(MatchingCompletion).getValue() = true and first(next, succ) | - next = this.getValue(0) + next = super.getValue(0) or - not exists(this.getValue(_)) and - next = this.getRestVariableAccess() + not exists(super.getValue(_)) and + next = super.getRestVariableAccess() ) or - last(max(int i | | this.getValue(i) order by i), pred, c) and - first(this.getRestVariableAccess(), succ) and + last(max(int i | | super.getValue(i) order by i), pred, c) and + first(super.getRestVariableAccess(), succ) and c.(MatchingCompletion).getValue() = true or exists(int i | - last(this.getValue(i), pred, c) and - first(this.getValue(i + 1), succ) and + last(super.getValue(i), pred, c) and + first(super.getValue(i + 1), succ) and c.(MatchingCompletion).getValue() = true ) } } - private class LineLiteralTree extends LeafTree, LineLiteral { } + private class LineLiteralTree extends LeafTree instanceof LineLiteral { } - private class FileLiteralTree extends LeafTree, FileLiteral { } + private class FileLiteralTree extends LeafTree instanceof FileLiteral { } - private class EncodingLiteralTree extends LeafTree, EncodingLiteral { } + private class EncodingLiteralTree extends LeafTree instanceof EncodingLiteral { } - private class AlternativePatternTree extends PreOrderTree, AlternativePattern { - final override predicate propagatesAbnormal(AstNode child) { child = this.getAnAlternative() } + private class AlternativePatternTree extends PreOrderTree instanceof AlternativePattern { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAnAlternative() } final override predicate last(AstNode last, Completion c) { - last(this.getAnAlternative(), last, c) and + last(super.getAnAlternative(), last, c) and c.(MatchingCompletion).getValue() = true or - last(max(int i | | this.getAlternative(i) order by i), last, c) and + last(max(int i | | super.getAlternative(i) order by i), last, c) and c.(MatchingCompletion).getValue() = false } final override predicate succ(AstNode pred, AstNode succ, Completion c) { pred = this and - first(this.getAlternative(0), succ) and + first(super.getAlternative(0), succ) and c instanceof SimpleCompletion or exists(int i | - last(this.getAlternative(i), pred, c) and - first(this.getAlternative(i + 1), succ) and + last(super.getAlternative(i), pred, c) and + first(super.getAlternative(i + 1), succ) and c.(MatchingCompletion).getValue() = false ) } } - private class AsPatternTree extends PreOrderTree, AsPattern { - final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern() } + private class AsPatternTree extends PreOrderTree instanceof AsPattern { + final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern() } final override predicate last(AstNode last, Completion c) { - last(this.getPattern(), last, c) and + last(super.getPattern(), last, c) and c.(MatchingCompletion).getValue() = false or - last(this.getVariableAccess(), last, any(SimpleCompletion x)) and + last(super.getVariableAccess(), last, any(SimpleCompletion x)) and c.(MatchingCompletion).getValue() = true and not c instanceof NestedCompletion } final override predicate succ(AstNode pred, AstNode succ, Completion c) { pred = this and - first(this.getPattern(), succ) and + first(super.getPattern(), succ) and c instanceof SimpleCompletion or - last(this.getPattern(), pred, c) and - first(this.getVariableAccess(), succ) and + last(super.getPattern(), pred, c) and + first(super.getVariableAccess(), succ) and c.(MatchingCompletion).getValue() = true } } - private class ParenthesizedPatternTree extends StandardPreOrderTree, ParenthesizedPattern { - override ControlFlowTree getChildElement(int i) { result = this.getPattern() and i = 0 } + private class ParenthesizedPatternTree extends StandardPreOrderTree instanceof ParenthesizedPattern + { + override ControlFlowTree getChildNode(int i) { result = super.getPattern() and i = 0 } } - private class ReferencePatternTree extends StandardPreOrderTree, ReferencePattern { - override ControlFlowTree getChildElement(int i) { result = this.getExpr() and i = 0 } + private class ReferencePatternTree extends StandardPreOrderTree instanceof ReferencePattern { + override ControlFlowTree getChildNode(int i) { result = super.getExpr() and i = 0 } } - private class InClauseTree extends PreOrderTree, InClause { + private class InClauseTree extends PreOrderTree instanceof InClause { final override predicate propagatesAbnormal(AstNode child) { - child = this.getPattern() or - child = this.getCondition() + child = super.getPattern() or + child = super.getCondition() } private predicate lastCondition(AstNode last, BooleanCompletion c, boolean flag) { - last(this.getCondition(), last, c) and + last(super.getCondition(), last, c) and ( - flag = true and this.hasIfCondition() + flag = true and super.hasIfCondition() or - flag = false and this.hasUnlessCondition() + flag = false and super.hasUnlessCondition() ) } final override predicate last(AstNode last, Completion c) { - last(this.getPattern(), last, c) and + last(super.getPattern(), last, c) and c.(MatchingCompletion).getValue() = false or exists(BooleanCompletion bc, boolean flag, MatchingCompletion mc | @@ -777,140 +814,140 @@ module Trees { mc.getValue() = false and bc.getValue() = flag.booleanNot() or - not exists(this.getBody()) and + not exists(super.getBody()) and mc.getValue() = true and bc.getValue() = flag ) or - last(this.getBody(), last, c) + last(super.getBody(), last, c) or - not exists(this.getBody()) and - not exists(this.getCondition()) and - last(this.getPattern(), last, c) + not exists(super.getBody()) and + not exists(super.getCondition()) and + last(super.getPattern(), last, c) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { pred = this and - first(this.getPattern(), succ) and + first(super.getPattern(), succ) and c instanceof SimpleCompletion or exists(Expr next | - last(this.getPattern(), pred, c) and + last(super.getPattern(), pred, c) and c.(MatchingCompletion).getValue() = true and first(next, succ) | - next = this.getCondition() + next = super.getCondition() or - not exists(this.getCondition()) and next = this.getBody() + not exists(super.getCondition()) and next = super.getBody() ) or exists(boolean flag | this.lastCondition(pred, c, flag) and c.(BooleanCompletion).getValue() = flag and - first(this.getBody(), succ) + first(super.getBody(), succ) ) } } - private class CharacterTree extends LeafTree, CharacterLiteral { } + private class CharacterTree extends LeafTree instanceof CharacterLiteral { } - private class ClassDeclarationTree extends NamespaceTree, ClassDeclaration { + private class ClassDeclarationTree extends NamespaceTree instanceof ClassDeclaration { /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getScopeExpr() and i = 0 and rescuable = false + result = super.getScopeExpr() and i = 0 and rescuable = false or - result = this.getSuperclassExpr() and - i = count(this.getScopeExpr()) and + result = super.getSuperclassExpr() and + i = count(super.getScopeExpr()) and rescuable = true or result = super - .getBodyChild(i - count(this.getScopeExpr()) - count(this.getSuperclassExpr()), + .getBodyChild(i - count(super.getScopeExpr()) - count(super.getSuperclassExpr()), rescuable) } } - private class ClassVariableTree extends LeafTree, ClassVariableAccess { } + private class ClassVariableTree extends LeafTree instanceof ClassVariableAccess { } - private class ConditionalExprTree extends PostOrderTree, ConditionalExpr { + private class ConditionalExprTree extends PostOrderTree instanceof ConditionalExpr { final override predicate propagatesAbnormal(AstNode child) { - child = this.getCondition() or child = this.getBranch(_) + child = super.getCondition() or child = super.getBranch(_) } - final override predicate first(AstNode first) { first(this.getCondition(), first) } + final override predicate first(AstNode first) { first(super.getCondition(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { exists(boolean b | - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and b = c.(BooleanCompletion).getValue() | - first(this.getBranch(b), succ) + first(super.getBranch(b), succ) or - not exists(this.getBranch(b)) and + not exists(super.getBranch(b)) and succ = this ) or - last(this.getBranch(_), pred, c) and + last(super.getBranch(_), pred, c) and succ = this and c instanceof NormalCompletion } } - private class ConditionalLoopTree extends PostOrderTree, ConditionalLoop { - final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() } + private class ConditionalLoopTree extends PostOrderTree instanceof ConditionalLoop { + final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() } - final override predicate first(AstNode first) { first(this.getCondition(), first) } + final override predicate first(AstNode first) { first(super.getCondition(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getCondition(), pred, c) and - this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and - first(this.getBody(), succ) + last(super.getCondition(), pred, c) and + super.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and + first(super.getBody(), succ) or - last(this.getBody(), pred, c) and - first(this.getCondition(), succ) and + last(super.getBody(), pred, c) and + first(super.getCondition(), succ) and c.continuesLoop() or - last(this.getBody(), pred, c) and - first(this.getBody(), succ) and + last(super.getBody(), pred, c) and + first(super.getBody(), succ) and c instanceof RedoCompletion or succ = this and ( - last(this.getCondition(), pred, c) and - this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot()) + last(super.getCondition(), pred, c) and + super.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot()) or - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and not c.continuesLoop() and not c instanceof BreakCompletion and not c instanceof RedoCompletion or - last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + last(super.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) ) } } - private class ConstantAccessTree extends PostOrderTree, ConstantAccess { + private class ConstantAccessTree extends PostOrderTree instanceof ConstantAccess { ConstantAccessTree() { not this instanceof ClassDeclaration and not this instanceof ModuleDeclaration and // constant accesses with scope expression in compound assignments are desugared not ( this = any(AssignOperation op).getLeftOperand() and - exists(this.getScopeExpr()) + exists(super.getScopeExpr()) ) } - final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() } + final override predicate propagatesAbnormal(AstNode child) { child = super.getScopeExpr() } final override predicate first(AstNode first) { - first(this.getScopeExpr(), first) + first(super.getScopeExpr(), first) or - not exists(this.getScopeExpr()) and + not exists(super.getScopeExpr()) and first = this } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getScopeExpr(), pred, c) and + last(super.getScopeExpr(), pred, c) and succ = this and c instanceof NormalCompletion } @@ -952,8 +989,9 @@ module Trees { } } - private class DestructuredParameterTree extends StandardPostOrderTree, DestructuredParameter { - final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) } + private class DestructuredParameterTree extends StandardPostOrderTree instanceof DestructuredParameter + { + final override ControlFlowTree getChildNode(int i) { result = super.getElement(i) } } private class DesugaredTree extends ControlFlowTree { @@ -972,155 +1010,158 @@ module Trees { final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } } - private class DoBlockTree extends BodyStmtTree, DoBlock { + private class DoBlockTree extends BodyStmtTree instanceof DoBlock { /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getParameter(i) and rescuable = false + result = super.getParameter(i) and rescuable = false or - result = this.getLocalVariable(i - this.getNumberOfParameters()) and rescuable = false + result = super.getLocalVariable(i - super.getNumberOfParameters()) and rescuable = false or result = BodyStmtTree.super - .getBodyChild(i - this.getNumberOfParameters() - count(this.getALocalVariable()), + .getBodyChild(i - super.getNumberOfParameters() - count(super.getALocalVariable()), rescuable) } override predicate propagatesAbnormal(AstNode child) { none() } } - private class EmptyStatementTree extends LeafTree, EmptyStmt { } + private class EmptyStatementTree extends LeafTree instanceof EmptyStmt { } - class EndBlockTree extends StmtSequenceTree, EndBlock { + class EndBlockTree extends StmtSequenceTree instanceof EndBlock { override predicate first(AstNode first) { first = this } override predicate succ(AstNode pred, AstNode succ, Completion c) { // Normal left-to-right evaluation in the body exists(int i | - last(this.getBodyChild(i, _), pred, c) and - first(this.getBodyChild(i + 1, _), succ) and + last(super.getBodyChild(i, _), pred, c) and + first(super.getBodyChild(i + 1, _), succ) and c instanceof NormalCompletion ) } } - private class ForwardedArgumentsTree extends LeafTree, ForwardedArguments { } + private class ForwardedArgumentsTree extends LeafTree instanceof ForwardedArguments { } - private class ForwardParameterTree extends LeafTree, ForwardParameter { } + private class ForwardParameterTree extends LeafTree instanceof ForwardParameter { } - private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { } + private class GlobalVariableTree extends LeafTree instanceof GlobalVariableAccess { } - private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { } + private class HashSplatNilParameterTree extends LeafTree instanceof HashSplatNilParameter { } - private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { } + private class HashSplatParameterTree extends NonDefaultValueParameterTree instanceof HashSplatParameter + { } - private class HereDocTree extends StandardPostOrderTree, HereDoc { - final override ControlFlowTree getChildElement(int i) { result = this.getComponent(i) } + private class HereDocTree extends StandardPostOrderTree instanceof HereDoc { + final override ControlFlowTree getChildNode(int i) { result = super.getComponent(i) } } - private class InstanceVariableTree extends StandardPostOrderTree, InstanceVariableAccess { - final override ControlFlowTree getChildElement(int i) { result = this.getReceiver() and i = 0 } + private class InstanceVariableTree extends StandardPostOrderTree instanceof InstanceVariableAccess + { + final override ControlFlowTree getChildNode(int i) { result = super.getReceiver() and i = 0 } } - private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { - final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + private class KeywordParameterTree extends DefaultValueParameterTree instanceof KeywordParameter { + final override Expr getDefaultValueExpr() { result = super.getDefaultValue() } - final override AstNode getAccessNode() { result = this.getDefiningAccess() } + final override AstNode getAccessNode() { result = super.getDefiningAccess() } } - private class LambdaTree extends BodyStmtTree, Lambda { + private class LambdaTree extends BodyStmtTree instanceof Lambda { final override predicate propagatesAbnormal(AstNode child) { none() } /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getParameter(i) and rescuable = false + result = super.getParameter(i) and rescuable = false or - result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtTree.super.getBodyChild(i - super.getNumberOfParameters(), rescuable) } } - private class LocalVariableAccessTree extends LeafTree, LocalVariableAccess { } + private class LocalVariableAccessTree extends LeafTree instanceof LocalVariableAccess { } - private class LogicalAndTree extends PostOrderTree, LogicalAndExpr { - final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + private class LogicalAndTree extends PostOrderTree instanceof LogicalAndExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() } - final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof TrueCompletion and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof FalseCompletion and succ = this or - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class LogicalNotTree extends PostOrderTree, NotExpr { - final override predicate propagatesAbnormal(AstNode child) { child = this.getOperand() } + private class LogicalNotTree extends PostOrderTree instanceof NotExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getOperand() } - final override predicate first(AstNode first) { first(this.getOperand(), first) } + final override predicate first(AstNode first) { first(super.getOperand(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { succ = this and - last(this.getOperand(), pred, c) and + last(super.getOperand(), pred, c) and c instanceof NormalCompletion } } - private class LogicalOrTree extends PostOrderTree, LogicalOrExpr { - final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + private class LogicalOrTree extends PostOrderTree instanceof LogicalOrExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() } - final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof FalseCompletion and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof TrueCompletion and succ = this or - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class MethodCallTree extends CallTree, MethodCall { - final override ControlFlowTree getChildElement(int i) { - result = this.getReceiver() and i = 0 + private class MethodCallTree extends CallTree instanceof MethodCall { + final override ControlFlowTree getChildNode(int i) { + result = super.getReceiver() and i = 0 or - result = this.getArgument(i - 1) + result = super.getArgument(i - 1) or - result = this.getBlock() and i = 1 + this.getNumberOfArguments() + result = super.getBlock() and i = 1 + super.getNumberOfArguments() } } - private class MethodNameTree extends LeafTree, MethodName, AstInternal::TTokenMethodName { } + private class MethodNameTree extends LeafTree instanceof MethodName, AstInternal::TTokenMethodName + { } - private class MethodTree extends BodyStmtTree, Method { + private class MethodTree extends BodyStmtTree instanceof Method { final override predicate propagatesAbnormal(AstNode child) { none() } /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getParameter(i) and rescuable = false + result = super.getParameter(i) and rescuable = false or - result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtTree.super.getBodyChild(i - super.getNumberOfParameters(), rescuable) } } - private class ModuleDeclarationTree extends NamespaceTree, ModuleDeclaration { + private class ModuleDeclarationTree extends NamespaceTree instanceof ModuleDeclaration { /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getScopeExpr() and i = 0 and rescuable = false + result = super.getScopeExpr() and i = 0 and rescuable = false or - result = NamespaceTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) + result = NamespaceTree.super.getBodyChild(i - count(super.getScopeExpr()), rescuable) } } @@ -1129,7 +1170,7 @@ module Trees { * executed in pre-order rather than post-order. We do this in order to insert a write for * `self` before any subsequent reads in the namespace body. */ - private class NamespaceTree extends BodyStmtTree, Namespace { + private class NamespaceTree extends BodyStmtTree instanceof Namespace { final override predicate first(AstNode first) { first = this } final override predicate succ(AstNode pred, AstNode succ, Completion c) { @@ -1149,79 +1190,80 @@ module Trees { } } - private class NilTree extends LeafTree, NilLiteral { } + private class NilTree extends LeafTree instanceof NilLiteral { } - private class NumericLiteralTree extends LeafTree, NumericLiteral { } + private class NumericLiteralTree extends LeafTree instanceof NumericLiteral { } - private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { - final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + private class OptionalParameterTree extends DefaultValueParameterTree instanceof OptionalParameter + { + final override Expr getDefaultValueExpr() { result = super.getDefaultValue() } - final override AstNode getAccessNode() { result = this.getDefiningAccess() } + final override AstNode getAccessNode() { result = super.getDefiningAccess() } } - private class PairTree extends StandardPostOrderTree, Pair { - final override ControlFlowTree getChildElement(int i) { - result = this.getKey() and i = 0 + private class PairTree extends StandardPostOrderTree instanceof Pair { + final override ControlFlowTree getChildNode(int i) { + result = super.getKey() and i = 0 or - result = this.getValue() and i = 1 + result = super.getValue() and i = 1 } } - private class RangeLiteralTree extends StandardPostOrderTree, RangeLiteral { - final override ControlFlowTree getChildElement(int i) { - result = this.getBegin() and i = 0 + private class RangeLiteralTree extends StandardPostOrderTree instanceof RangeLiteral { + final override ControlFlowTree getChildNode(int i) { + result = super.getBegin() and i = 0 or - result = this.getEnd() and i = 1 + result = super.getEnd() and i = 1 } } - private class RedoStmtTree extends LeafTree, RedoStmt { } + private class RedoStmtTree extends LeafTree instanceof RedoStmt { } - private class RescueModifierTree extends PostOrderTree, RescueModifierExpr { - final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } + private class RescueModifierTree extends PostOrderTree instanceof RescueModifierExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getHandler() } - final override predicate first(AstNode first) { first(this.getBody(), first) } + final override predicate first(AstNode first) { first(super.getBody(), first) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and ( c instanceof RaiseCompletion and - first(this.getHandler(), succ) + first(super.getHandler(), succ) or not c instanceof RaiseCompletion and succ = this ) or - last(this.getHandler(), pred, c) and + last(super.getHandler(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class RescueTree extends PostOrderTree, RescueClause { + private class RescueTree extends PostOrderTree instanceof RescueClause { final override predicate propagatesAbnormal(AstNode child) { - child = this.getAnException() or - child = this.getBody() + child = super.getAnException() or + child = super.getBody() } final override predicate first(AstNode first) { - first(this.getException(0), first) + first(super.getException(0), first) or - not exists(this.getException(0)) and + not exists(super.getException(0)) and ( - first(this.getVariableExpr(), first) + first(super.getVariableExpr(), first) or - not exists(this.getVariableExpr()) and + not exists(super.getVariableExpr()) and ( - first(this.getBody(), first) + first(super.getBody(), first) or - not exists(this.getBody()) and first = this + not exists(super.getBody()) and first = this ) ) } private Expr getLastException() { - exists(int i | result = this.getException(i) and not exists(this.getException(i + 1))) + exists(int i | result = super.getException(i) and not exists(super.getException(i + 1))) } predicate lastNoMatch(AstNode last, Completion c) { @@ -1230,54 +1272,55 @@ module Trees { } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getAnException(), pred, c) and + last(super.getAnException(), pred, c) and c.(MatchingCompletion).getValue() = true and ( - first(this.getVariableExpr(), succ) + first(super.getVariableExpr(), succ) or - not exists(this.getVariableExpr()) and + not exists(super.getVariableExpr()) and ( - first(this.getBody(), succ) + first(super.getBody(), succ) or - not exists(this.getBody()) and succ = this + not exists(super.getBody()) and succ = this ) ) or exists(int i | - last(this.getException(i), pred, c) and + last(super.getException(i), pred, c) and c.(MatchingCompletion).getValue() = false and - first(this.getException(i + 1), succ) + first(super.getException(i + 1), succ) ) or - last(this.getVariableExpr(), pred, c) and + last(super.getVariableExpr(), pred, c) and c instanceof NormalCompletion and ( - first(this.getBody(), succ) + first(super.getBody(), succ) or - not exists(this.getBody()) and succ = this + not exists(super.getBody()) and succ = this ) or - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class RetryStmtTree extends LeafTree, RetryStmt { } + private class RetryStmtTree extends LeafTree instanceof RetryStmt { } - private class ReturningStmtTree extends StandardPostOrderTree, ReturningStmt { - final override ControlFlowTree getChildElement(int i) { result = this.getValue() and i = 0 } + private class ReturningStmtTree extends StandardPostOrderTree instanceof ReturningStmt { + final override ControlFlowTree getChildNode(int i) { result = super.getValue() and i = 0 } } - private class SimpleParameterTree extends NonDefaultValueParameterTree, SimpleParameter { } + private class SimpleParameterTree extends NonDefaultValueParameterTree instanceof SimpleParameter { + } // Corner case: For duplicated '_' parameters, only the first occurrence has a defining // access. For subsequent parameters we simply include the parameter itself in the CFG - private class SimpleParameterTreeDupUnderscore extends LeafTree, SimpleParameter { + private class SimpleParameterTreeDupUnderscore extends LeafTree instanceof SimpleParameter { SimpleParameterTreeDupUnderscore() { not exists(this.getDefiningAccess()) } } - private class SingletonClassTree extends BodyStmtTree, SingletonClass { + private class SingletonClassTree extends BodyStmtTree instanceof SingletonClass { final override predicate first(AstNode first) { this.firstInner(first) or @@ -1295,50 +1338,51 @@ module Trees { /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { ( - result = this.getValue() and i = 0 and rescuable = false + result = super.getValue() and i = 0 and rescuable = false or result = BodyStmtTree.super.getBodyChild(i - 1, rescuable) ) } } - private class SingletonMethodTree extends BodyStmtTree, SingletonMethod { + private class SingletonMethodTree extends BodyStmtTree instanceof SingletonMethod { final override predicate propagatesAbnormal(AstNode child) { none() } /** Gets the `i`th child in the body of this block. */ final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getParameter(i) and rescuable = false + result = super.getParameter(i) and rescuable = false or - result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtTree.super.getBodyChild(i - super.getNumberOfParameters(), rescuable) } - override predicate first(AstNode first) { first(this.getObject(), first) } + override predicate first(AstNode first) { first(super.getObject(), first) } override predicate succ(AstNode pred, AstNode succ, Completion c) { BodyStmtTree.super.succ(pred, succ, c) or - last(this.getObject(), pred, c) and + last(super.getObject(), pred, c) and succ = this and c instanceof NormalCompletion } } - private class SplatParameterTree extends NonDefaultValueParameterTree, SplatParameter { } + private class SplatParameterTree extends NonDefaultValueParameterTree instanceof SplatParameter { + } - class StmtSequenceTree extends PostOrderTree, StmtSequence { - override predicate propagatesAbnormal(AstNode child) { child = this.getAStmt() } + class StmtSequenceTree extends PostOrderTree instanceof StmtSequence { + override predicate propagatesAbnormal(AstNode child) { child = super.getAStmt() } override predicate first(AstNode first) { // If this sequence contains any statements, go to the first one. - first(this.getStmt(0), first) + first(super.getStmt(0), first) or // Otherwise, treat this node as a leaf node. - not exists(this.getStmt(0)) and first = this + not exists(super.getStmt(0)) and first = this } /** Gets the `i`th child in the body of this body statement. */ AstNode getBodyChild(int i, boolean rescuable) { - result = this.getStmt(i) and + result = super.getStmt(i) and rescuable = true } @@ -1363,17 +1407,18 @@ module Trees { } } - private class StringConcatenationTree extends StandardPostOrderTree, StringConcatenation { - final override ControlFlowTree getChildElement(int i) { result = this.getString(i) } + private class StringConcatenationTree extends StandardPostOrderTree instanceof StringConcatenation + { + final override ControlFlowTree getChildNode(int i) { result = super.getString(i) } } - private class StringlikeLiteralTree extends StandardPostOrderTree, StringlikeLiteral { + private class StringlikeLiteralTree extends StandardPostOrderTree instanceof StringlikeLiteral { StringlikeLiteralTree() { not this instanceof HereDoc } - final override ControlFlowTree getChildElement(int i) { result = this.getComponent(i) } + final override ControlFlowTree getChildNode(int i) { result = super.getComponent(i) } } - private class StringComponentTree extends LeafTree, StringComponent { + private class StringComponentTree extends LeafTree instanceof StringComponent { StringComponentTree() { // Interpolations contain `StmtSequence`s, so they shouldn't be treated as leaf nodes. not this instanceof StringInterpolationComponent and @@ -1383,52 +1428,52 @@ module Trees { } } - private class ToplevelTree extends BodyStmtTree, Toplevel { + private class ToplevelTree extends BodyStmtTree instanceof Toplevel { final override AstNode getBodyChild(int i, boolean rescuable) { - result = this.getBeginBlock(i) and rescuable = true + result = super.getBeginBlock(i) and rescuable = true or - result = BodyStmtTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) + result = BodyStmtTree.super.getBodyChild(i - count(super.getABeginBlock()), rescuable) } - final override predicate first(AstNode first) { this.firstInner(first) } + final override predicate first(AstNode first) { super.firstInner(first) } - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + final override predicate last(AstNode last, Completion c) { super.lastInner(last, c) } } - private class UndefStmtTree extends StandardPreOrderTree, UndefStmt { - final override ControlFlowTree getChildElement(int i) { result = this.getMethodName(i) } + private class UndefStmtTree extends StandardPreOrderTree instanceof UndefStmt { + final override ControlFlowTree getChildNode(int i) { result = super.getMethodName(i) } } - private class WhenTree extends ControlFlowTree, WhenClause { + private class WhenTree extends ControlFlowTree instanceof WhenClause { final override predicate propagatesAbnormal(AstNode child) { - child = [this.getAPattern(), this.getBody()] + child = [super.getAPattern(), super.getBody()] } final Expr getLastPattern() { exists(int i | - result = this.getPattern(i) and - not exists(this.getPattern(i + 1)) + result = super.getPattern(i) and + not exists(super.getPattern(i + 1)) ) } - final override predicate first(AstNode first) { first(this.getPattern(0), first) } + final override predicate first(AstNode first) { first(super.getPattern(0), first) } final override predicate last(AstNode last, Completion c) { last = this and c.isValidFor(this) and c.(ConditionalCompletion).getValue() = false or - last(this.getBody(), last, c) + last(super.getBody(), last, c) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { pred = this and c.isValidFor(this) and c.(ConditionalCompletion).getValue() = true and - first(this.getBody(), succ) + first(super.getBody(), succ) or exists(int i, Expr p, boolean b | - p = this.getPattern(i) and + p = super.getPattern(i) and last(p, pred, c) and b = c.(ConditionalCompletion).getValue() | @@ -1436,9 +1481,9 @@ module Trees { succ = this or b = false and - first(this.getPattern(i + 1), succ) + first(super.getPattern(i + 1), succ) or - not exists(this.getPattern(i + 1)) and + not exists(super.getPattern(i + 1)) and succ = this ) } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll deleted file mode 100644 index 5039d09ff22..00000000000 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll +++ /dev/null @@ -1,1027 +0,0 @@ -/** Provides language-independent definitions for AST-to-CFG construction. */ - -private import ControlFlowGraphImplSpecific - -/** An element with associated control flow. */ -abstract class ControlFlowTree extends ControlFlowTreeBase { - /** Holds if `first` is the first element executed within this element. */ - pragma[nomagic] - abstract predicate first(ControlFlowElement first); - - /** - * Holds if `last` with completion `c` is a potential last element executed - * within this element. - */ - pragma[nomagic] - abstract predicate last(ControlFlowElement last, Completion c); - - /** Holds if abnormal execution of `child` should propagate upwards. */ - abstract predicate propagatesAbnormal(ControlFlowElement child); - - /** - * Holds if `succ` is a control flow successor for `pred`, given that `pred` - * finishes with completion `c`. - */ - pragma[nomagic] - abstract predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c); -} - -/** - * Holds if `first` is the first element executed within control flow - * element `cft`. - */ -predicate first(ControlFlowTree cft, ControlFlowElement first) { cft.first(first) } - -/** - * Holds if `last` with completion `c` is a potential last element executed - * within control flow element `cft`. - */ -predicate last(ControlFlowTree cft, ControlFlowElement last, Completion c) { - cft.last(last, c) - or - exists(ControlFlowElement cfe | - cft.propagatesAbnormal(cfe) and - last(cfe, last, c) and - not completionIsNormal(c) - ) -} - -/** - * Holds if `succ` is a control flow successor for `pred`, given that `pred` - * finishes with completion `c`. - */ -pragma[nomagic] -predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - any(ControlFlowTree cft).succ(pred, succ, c) -} - -/** An element that is executed in pre-order. */ -abstract class PreOrderTree extends ControlFlowTree { - final override predicate first(ControlFlowElement first) { first = this } -} - -/** An element that is executed in post-order. */ -abstract class PostOrderTree extends ControlFlowTree { - override predicate last(ControlFlowElement last, Completion c) { - last = this and - completionIsValidFor(c, last) - } -} - -/** - * An element where the children are evaluated following a standard left-to-right - * evaluation. The actual evaluation order is determined by the predicate - * `getChildElement()`. - */ -abstract class StandardTree extends ControlFlowTree { - /** Gets the `i`th child element, in order of evaluation. */ - abstract ControlFlowElement getChildElement(int i); - - private ControlFlowElement getChildElementRanked(int i) { - result = - rank[i + 1](ControlFlowElement child, int j | - child = this.getChildElement(j) - | - child order by j - ) - } - - /** Gets the first child node of this element. */ - final ControlFlowElement getFirstChildElement() { result = this.getChildElementRanked(0) } - - /** Gets the last child node of this node. */ - final ControlFlowElement getLastChildElement() { - exists(int last | - result = this.getChildElementRanked(last) and - not exists(this.getChildElementRanked(last + 1)) - ) - } - - /** Holds if this element has no children. */ - predicate isLeafElement() { not exists(this.getFirstChildElement()) } - - override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getChildElement(_) - } - - pragma[nomagic] - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - exists(int i | - last(this.getChildElementRanked(i), pred, c) and - completionIsNormal(c) and - first(this.getChildElementRanked(i + 1), succ) - ) - } -} - -/** A standard element that is executed in pre-order. */ -abstract class StandardPreOrderTree extends StandardTree, PreOrderTree { - override predicate last(ControlFlowElement last, Completion c) { - last(this.getLastChildElement(), last, c) - or - this.isLeafElement() and - completionIsValidFor(c, this) and - last = this - } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - StandardTree.super.succ(pred, succ, c) - or - pred = this and - first(this.getFirstChildElement(), succ) and - completionIsSimple(c) - } -} - -/** A standard element that is executed in post-order. */ -abstract class StandardPostOrderTree extends StandardTree, PostOrderTree { - override predicate first(ControlFlowElement first) { - first(this.getFirstChildElement(), first) - or - not exists(this.getFirstChildElement()) and - first = this - } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - StandardTree.super.succ(pred, succ, c) - or - last(this.getLastChildElement(), pred, c) and - succ = this and - completionIsNormal(c) - } -} - -/** An element that is a leaf in the control flow graph. */ -abstract class LeafTree extends PreOrderTree, PostOrderTree { - override predicate propagatesAbnormal(ControlFlowElement child) { none() } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { none() } -} - -/** - * Holds if split kinds `sk1` and `sk2` may overlap. That is, they may apply - * to at least one common AST node inside `scope`. - */ -private predicate overlapping(CfgScope scope, SplitKind sk1, SplitKind sk2) { - exists(ControlFlowElement e | - sk1.appliesTo(e) and - sk2.appliesTo(e) and - scope = getCfgScope(e) - ) -} - -/** - * A split kind. Each control flow node can have at most one split of a - * given kind. - */ -abstract class SplitKind extends SplitKindBase { - /** Gets a split of this kind. */ - SplitImpl getASplit() { result.getKind() = this } - - /** Holds if some split of this kind applies to AST node `n`. */ - predicate appliesTo(ControlFlowElement n) { this.getASplit().appliesTo(n) } - - /** - * Gets a unique integer representing this split kind. The integer is used - * to represent sets of splits as ordered lists. - */ - abstract int getListOrder(); - - /** Gets the rank of this split kind among all overlapping kinds for `c`. */ - private int getRank(CfgScope scope) { - this = rank[result](SplitKind sk | overlapping(scope, this, sk) | sk order by sk.getListOrder()) - } - - /** - * Holds if this split kind is enabled for AST node `n`. For performance reasons, - * the number of splits is restricted by the `maxSplits()` predicate. - */ - predicate isEnabled(ControlFlowElement n) { - this.appliesTo(n) and - this.getRank(getCfgScope(n)) <= maxSplits() - } - - /** - * Gets the rank of this split kind among all the split kinds that apply to - * AST node `n`. The rank is based on the order defined by `getListOrder()`. - */ - int getListRank(ControlFlowElement n) { - this.isEnabled(n) and - this = rank[result](SplitKind sk | sk.appliesTo(n) | sk order by sk.getListOrder()) - } - - /** Gets a textual representation of this split kind. */ - abstract string toString(); -} - -/** An interface for implementing an entity to split on. */ -abstract class SplitImpl extends Split { - /** Gets the kind of this split. */ - abstract SplitKind getKind(); - - /** - * Holds if this split is entered when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasEntry(pred, succ, c) implies succ(pred, succ, c)`. - */ - abstract predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** - * Holds if this split is entered when control passes from `scope` to the entry point - * `first`. - * - * Invariant: `hasEntryScope(scope, first) implies scopeFirst(scope, first)`. - */ - abstract predicate hasEntryScope(CfgScope scope, ControlFlowElement first); - - /** - * Holds if this split is left when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasExit(pred, succ, c) implies succ(pred, succ, c)`. - */ - abstract predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** - * Holds if this split is left when control passes from `last` out of the enclosing - * scope `scope` with completion `c`. - * - * Invariant: `hasExitScope(scope, last, c) implies scopeLast(scope, last, c)` - */ - abstract predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c); - - /** - * Holds if this split is maintained when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasSuccessor(pred, succ, c) implies succ(pred, succ, c)` - */ - abstract predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** Holds if this split applies to control flow element `cfe`. */ - final predicate appliesTo(ControlFlowElement cfe) { - this.hasEntry(_, cfe, _) - or - this.hasEntryScope(_, cfe) - or - exists(ControlFlowElement pred | this.appliesTo(pred) | this.hasSuccessor(pred, cfe, _)) - } - - /** The `succ` relation restricted to predecessors `pred` that this split applies to. */ - pragma[noinline] - final predicate appliesSucc(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - this.appliesTo(pred) and - succ(pred, succ, c) - } -} - -/** - * A set of control flow node splits. The set is represented by a list of splits, - * ordered by ascending rank. - */ -class Splits extends TSplits { - /** Gets a textual representation of this set of splits. */ - string toString() { result = splitsToString(this) } - - /** Gets a split belonging to this set of splits. */ - SplitImpl getASplit() { - exists(SplitImpl head, Splits tail | this = TSplitsCons(head, tail) | - result = head - or - result = tail.getASplit() - ) - } -} - -private predicate succEntrySplitsFromRank( - CfgScope pred, ControlFlowElement succ, Splits splits, int rnk -) { - splits = TSplitsNil() and - scopeFirst(pred, succ) and - rnk = 0 - or - exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | - splits = TSplitsCons(head, tail) - ) -} - -private predicate succEntrySplitsCons( - CfgScope pred, ControlFlowElement succ, SplitImpl head, Splits tail, int rnk -) { - succEntrySplitsFromRank(pred, succ, tail, rnk - 1) and - head.hasEntryScope(pred, succ) and - rnk = head.getKind().getListRank(succ) -} - -/** - * Holds if `succ` with splits `succSplits` is the first element that is executed - * when entering callable `pred`. - */ -pragma[noinline] -private predicate succEntrySplits( - CfgScope pred, ControlFlowElement succ, Splits succSplits, SuccessorType t -) { - exists(int rnk | - scopeFirst(pred, succ) and - successorTypeIsSimple(t) and - succEntrySplitsFromRank(pred, succ, succSplits, rnk) - | - rnk = 0 and - not any(SplitImpl split).hasEntryScope(pred, succ) - or - rnk = max(SplitImpl split | split.hasEntryScope(pred, succ) | split.getKind().getListRank(succ)) - ) -} - -/** - * Holds if `pred` with splits `predSplits` can exit the enclosing callable - * `succ` with type `t`. - */ -private predicate succExitSplits( - ControlFlowElement pred, Splits predSplits, CfgScope succ, SuccessorType t -) { - exists(Reachability::SameSplitsBlock b, Completion c | pred = b.getAnElement() | - b.isReachable(succ, predSplits) and - t = getAMatchingSuccessorType(c) and - scopeLast(succ, pred, c) and - forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | - predSplit.hasExitScope(succ, pred, c) - ) - ) -} - -/** - * Provides a predicate for the successor relation with split information, - * as well as logic used to construct the type `TSplits` representing sets - * of splits. Only sets of splits that can be reached are constructed, hence - * the predicates are mutually recursive. - * - * For the successor relation - * - * ```ql - * succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) - * ``` - * - * the following invariants are maintained: - * - * 1. `pred` is reachable with split set `predSplits`. - * 2. For all `split` in `predSplits`: - * - If `split.hasSuccessor(pred, succ, c)` then `split` in `succSplits`. - * 3. For all `split` in `predSplits`: - * - If `split.hasExit(pred, succ, c)` and not `split.hasEntry(pred, succ, c)` then - * `split` not in `succSplits`. - * 4. For all `split` with kind not in `predSplits`: - * - If `split.hasEntry(pred, succ, c)` then `split` in `succSplits`. - * 5. For all `split` in `succSplits`: - * - `split.hasSuccessor(pred, succ, c)` and `split` in `predSplits`, or - * - `split.hasEntry(pred, succ, c)`. - * - * The algorithm divides into four cases: - * - * 1. The set of splits for the successor is the same as the set of splits - * for the predecessor: - * a) The successor is in the same `SameSplitsBlock` as the predecessor. - * b) The successor is *not* in the same `SameSplitsBlock` as the predecessor. - * 2. The set of splits for the successor is different from the set of splits - * for the predecessor: - * a) The set of splits for the successor is *maybe* non-empty. - * b) The set of splits for the successor is *always* empty. - * - * Only case 2a may introduce new sets of splits, so only predicates from - * this case are used in the definition of `TSplits`. - * - * The predicates in this module are named after the cases above. - */ -private module SuccSplits { - private predicate succInvariant1( - Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, - ControlFlowElement succ, Completion c - ) { - pred = b.getAnElement() and - b.isReachable(_, predSplits) and - succ(pred, succ, c) - } - - private predicate case1b0( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - exists(Reachability::SameSplitsBlock b | - // Invariant 1 - succInvariant1(b, pred, predSplits, succ, c) - | - (succ = b.getAnElement() implies succ = b) and - // Invariant 4 - not exists(SplitImpl split | split.hasEntry(pred, succ, c)) - ) - } - - /** - * Case 1b. - * - * Invariants 1 and 4 hold in the base case, and invariants 2, 3, and 5 are - * maintained for all splits in `predSplits` (= `succSplits`), except - * possibly for the splits in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case1bForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except - ) { - case1b0(pred, predSplits, succ, c) and - except = predSplits - or - exists(SplitImpl split | - case1bForallCons(pred, predSplits, succ, c, split, except) and - split.hasSuccessor(pred, succ, c) - ) - } - - pragma[noinline] - private predicate case1bForallCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, - SplitImpl exceptHead, Splits exceptTail - ) { - case1bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) - } - - private predicate case1( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - // Case 1a - exists(Reachability::SameSplitsBlock b | succInvariant1(b, pred, predSplits, succ, c) | - succ = b.getAnElement() and - not succ = b - ) - or - // Case 1b - case1bForall(pred, predSplits, succ, c, TSplitsNil()) - } - - pragma[noinline] - private SplitImpl succInvariant1GetASplit( - Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, - ControlFlowElement succ, Completion c - ) { - succInvariant1(b, pred, predSplits, succ, c) and - result = predSplits.getASplit() - } - - private predicate case2aux( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - exists(Reachability::SameSplitsBlock b | - succInvariant1(b, pred, predSplits, succ, c) and - (succ = b.getAnElement() implies succ = b) - | - succInvariant1GetASplit(b, pred, predSplits, succ, c).hasExit(pred, succ, c) - or - any(SplitImpl split).hasEntry(pred, succ, c) - ) - } - - /** - * Holds if `succSplits` should not inherit a split of kind `sk` from - * `predSplits`, except possibly because of a split in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case2aNoneInheritedOfKindForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk, - Splits except - ) { - case2aux(pred, predSplits, succ, c) and - sk.appliesTo(succ) and - except = predSplits - or - exists(Splits mid, SplitImpl split | - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, mid) and - mid = TSplitsCons(split, except) - | - split.getKind() = any(SplitKind sk0 | sk0 != sk and sk0.appliesTo(succ)) - or - split.hasExit(pred, succ, c) - ) - } - - pragma[nomagic] - private predicate entryOfKind( - ControlFlowElement pred, ControlFlowElement succ, Completion c, SplitImpl split, SplitKind sk - ) { - split.hasEntry(pred, succ, c) and - sk = split.getKind() - } - - /** Holds if `succSplits` should not have a split of kind `sk`. */ - pragma[nomagic] - private predicate case2aNoneOfKind( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk - ) { - // None inherited from predecessor - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and - // None newly entered into - not entryOfKind(pred, succ, c, _, sk) - } - - /** Holds if `succSplits` should not have a split of kind `sk` at rank `rnk`. */ - pragma[nomagic] - private predicate case2aNoneAtRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - ) { - exists(SplitKind sk | case2aNoneOfKind(pred, predSplits, succ, c, sk) | - rnk = sk.getListRank(succ) - ) - } - - pragma[nomagic] - private SplitImpl case2auxGetAPredecessorSplit( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - case2aux(pred, predSplits, succ, c) and - result = predSplits.getASplit() - } - - /** Gets a split that should be in `succSplits`. */ - pragma[nomagic] - private SplitImpl case2aSome( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk - ) { - ( - // Inherited from predecessor - result = case2auxGetAPredecessorSplit(pred, predSplits, succ, c) and - result.hasSuccessor(pred, succ, c) - or - // Newly entered into - exists(SplitKind sk0 | - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk0, TSplitsNil()) - | - entryOfKind(pred, succ, c, result, sk0) - ) - ) and - sk = result.getKind() - } - - /** Gets a split that should be in `succSplits` at rank `rnk`. */ - pragma[nomagic] - SplitImpl case2aSomeAtRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - ) { - exists(SplitKind sk | result = case2aSome(pred, predSplits, succ, c, sk) | - rnk = sk.getListRank(succ) - ) - } - - /** - * Case 2a. - * - * As opposed to the other cases, in this case we need to construct a new set - * of splits `succSplits`. Since this involves constructing the very IPA type, - * we cannot recurse directly over the structure of `succSplits`. Instead, we - * recurse over the ranks of all splits that *might* be in `succSplits`. - * - * - Invariant 1 holds in the base case, - * - invariant 2 holds for all splits with rank at least `rnk`, - * - invariant 3 holds for all splits in `predSplits`, - * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`, - * and - * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`. - */ - predicate case2aFromRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c, int rnk - ) { - case2aux(pred, predSplits, succ, c) and - succSplits = TSplitsNil() and - rnk = max(any(SplitKind sk).getListRank(succ)) + 1 - or - case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and - case2aNoneAtRank(pred, predSplits, succ, c, rnk) - or - exists(Splits mid, SplitImpl split | split = case2aCons(pred, predSplits, succ, mid, c, rnk) | - succSplits = TSplitsCons(split, mid) - ) - } - - pragma[noinline] - private SplitImpl case2aCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c, int rnk - ) { - case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and - result = case2aSomeAtRank(pred, predSplits, succ, c, rnk) - } - - /** - * Case 2b. - * - * Invariants 1, 4, and 5 hold in the base case, and invariants 2 and 3 are - * maintained for all splits in `predSplits`, except possibly for the splits - * in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case2bForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except - ) { - // Invariant 1 - case2aux(pred, predSplits, succ, c) and - // Invariants 4 and 5 - not any(SplitKind sk).appliesTo(succ) and - except = predSplits - or - exists(SplitImpl split | case2bForallCons(pred, predSplits, succ, c, split, except) | - // Invariants 2 and 3 - split.hasExit(pred, succ, c) - ) - } - - pragma[noinline] - private predicate case2bForallCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, - SplitImpl exceptHead, Splits exceptTail - ) { - case2bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) - } - - private predicate case2( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c - ) { - case2aFromRank(pred, predSplits, succ, succSplits, c, 1) - or - case2bForall(pred, predSplits, succ, c, TSplitsNil()) and - succSplits = TSplitsNil() - } - - /** - * Holds if `succ` with splits `succSplits` is a successor of type `t` for `pred` - * with splits `predSplits`. - */ - predicate succSplits( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c - ) { - case1(pred, predSplits, succ, c) and - succSplits = predSplits - or - case2(pred, predSplits, succ, succSplits, c) - } -} - -import SuccSplits - -/** Provides logic for calculating reachable control flow nodes. */ -private module Reachability { - /** - * Holds if `cfe` is a control flow element where the set of possible splits may - * be different from the set of possible splits for one of `cfe`'s predecessors. - * That is, `cfe` starts a new block of elements with the same set of splits. - */ - private predicate startsSplits(ControlFlowElement cfe) { - scopeFirst(_, cfe) - or - exists(SplitImpl s | - s.hasEntry(_, cfe, _) - or - s.hasExit(_, cfe, _) - ) - or - exists(ControlFlowElement pred, SplitImpl split, Completion c | succ(pred, cfe, c) | - split.appliesTo(pred) and - not split.hasSuccessor(pred, cfe, c) - ) - } - - private predicate intraSplitsSucc(ControlFlowElement pred, ControlFlowElement succ) { - succ(pred, succ, _) and - not startsSplits(succ) - } - - private predicate splitsBlockContains(ControlFlowElement start, ControlFlowElement cfe) = - fastTC(intraSplitsSucc/2)(start, cfe) - - /** - * A block of control flow elements where the set of splits is guaranteed - * to remain unchanged, represented by the first element in the block. - */ - class SameSplitsBlock extends ControlFlowElement { - SameSplitsBlock() { startsSplits(this) } - - /** Gets a control flow element in this block. */ - ControlFlowElement getAnElement() { - splitsBlockContains(this, result) - or - result = this - } - - pragma[noinline] - private SameSplitsBlock getASuccessor(Splits predSplits, Splits succSplits) { - exists(ControlFlowElement pred | pred = this.getAnElement() | - succSplits(pred, predSplits, result, succSplits, _) - ) - } - - /** - * Holds if the elements of this block are reachable from a callable entry - * point, with the splits `splits`. - */ - predicate isReachable(CfgScope scope, Splits splits) { - // Base case - succEntrySplits(scope, this, splits, _) - or - // Recursive case - exists(SameSplitsBlock pred, Splits predSplits | pred.isReachable(scope, predSplits) | - this = pred.getASuccessor(predSplits, splits) - ) - } - } -} - -cached -private module Cached { - /** - * If needed, call this predicate from `ControlFlowGraphImplSpecific.qll` in order to - * force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - newtype TSplits = - TSplitsNil() or - TSplitsCons(SplitImpl head, Splits tail) { - exists( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - | - case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) and - head = case2aSomeAtRank(pred, predSplits, succ, c, rnk) - ) - or - succEntrySplitsCons(_, _, head, tail, _) - } - - cached - string splitsToString(Splits splits) { - splits = TSplitsNil() and - result = "" - or - exists(SplitImpl head, Splits tail, string headString, string tailString | - splits = TSplitsCons(head, tail) - | - headString = head.toString() and - tailString = tail.toString() and - if tailString = "" - then result = headString - else - if headString = "" - then result = tailString - else result = headString + ", " + tailString - ) - } - - /** - * Internal representation of control flow nodes in the control flow graph. - * The control flow graph is pruned for unreachable nodes. - */ - cached - newtype TCfgNode = - TEntryNode(CfgScope scope) { succEntrySplits(scope, _, _, _) } or - TAnnotatedExitNode(CfgScope scope, boolean normal) { - exists(Reachability::SameSplitsBlock b, SuccessorType t | b.isReachable(scope, _) | - succExitSplits(b.getAnElement(), _, scope, t) and - if isAbnormalExitType(t) then normal = false else normal = true - ) - } or - TExitNode(CfgScope scope) { - exists(Reachability::SameSplitsBlock b | b.isReachable(scope, _) | - succExitSplits(b.getAnElement(), _, scope, _) - ) - } or - TElementNode(CfgScope scope, ControlFlowElement cfe, Splits splits) { - exists(Reachability::SameSplitsBlock b | b.isReachable(scope, splits) | - cfe = b.getAnElement() - ) - } - - /** Gets a successor node of a given flow type, if any. */ - cached - TCfgNode getASuccessor(TCfgNode pred, SuccessorType t) { - // Callable entry node -> callable body - exists(ControlFlowElement succElement, Splits succSplits, CfgScope scope | - result = TElementNode(scope, succElement, succSplits) and - pred = TEntryNode(scope) and - succEntrySplits(scope, succElement, succSplits, t) - ) - or - exists(CfgScope scope, ControlFlowElement predElement, Splits predSplits | - pred = TElementNode(pragma[only_bind_into](scope), predElement, predSplits) - | - // Element node -> callable exit (annotated) - exists(boolean normal | - result = TAnnotatedExitNode(pragma[only_bind_into](scope), normal) and - succExitSplits(predElement, predSplits, scope, t) and - if isAbnormalExitType(t) then normal = false else normal = true - ) - or - // Element node -> element node - exists(ControlFlowElement succElement, Splits succSplits, Completion c | - result = TElementNode(pragma[only_bind_into](scope), succElement, succSplits) - | - succSplits(predElement, predSplits, succElement, succSplits, c) and - t = getAMatchingSuccessorType(c) - ) - ) - or - // Callable exit (annotated) -> callable exit - exists(CfgScope scope | - pred = TAnnotatedExitNode(scope, _) and - result = TExitNode(scope) and - successorTypeIsSimple(t) - ) - } - - /** - * Gets a first control flow element executed within `cfe`. - */ - cached - ControlFlowElement getAControlFlowEntryNode(ControlFlowElement cfe) { first(cfe, result) } - - /** - * Gets a potential last control flow element executed within `cfe`. - */ - cached - ControlFlowElement getAControlFlowExitNode(ControlFlowElement cfe) { last(cfe, result, _) } - - /** - * Gets the CFG scope of node `n`. Unlike `getCfgScope`, this predicate - * is calculated based on reachability from an entry node, and it may - * yield different results for AST elements that are split into multiple - * scopes. - */ - cached - CfgScope getNodeCfgScope(TCfgNode n) { - n = TEntryNode(result) - or - n = TAnnotatedExitNode(result, _) - or - n = TExitNode(result) - or - n = TElementNode(result, _, _) - } -} - -import Cached - -/** - * Import this module into a `.ql` file of `@kind graph` to render a CFG. The - * graph is restricted to nodes from `RelevantNode`. - */ -module TestOutput { - abstract class RelevantNode extends Node { - /** - * Gets a string used to resolve ties in node and edge ordering. - */ - string getOrderDisambiguation() { result = "" } - } - - query predicate nodes(RelevantNode n, string attr, string val) { - attr = "semmle.order" and - val = - any(int i | - n = - rank[i](RelevantNode p, Location l | - l = p.getLocation() - | - p - order by - l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), - p.getOrderDisambiguation() - ) - ).toString() - } - - query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) { - attr = "semmle.label" and - val = - strictconcat(SuccessorType t, string s | - succ = getASuccessor(pred, t) and - if successorTypeIsSimple(t) then s = "" else s = t.toString() - | - s, ", " order by s - ) - or - attr = "semmle.order" and - val = - any(int i | - succ = - rank[i](RelevantNode s, SuccessorType t, Location l | - s = getASuccessor(pred, t) and - l = s.getLocation() - | - s - order by - l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), - s.getOrderDisambiguation() - ) - ).toString() - } -} - -/** Provides a set of splitting-related consistency queries. */ -module Consistency { - query predicate nonUniqueSetRepresentation(Splits s1, Splits s2) { - forex(Split s | s = s1.getASplit() | s = s2.getASplit()) and - forex(Split s | s = s2.getASplit() | s = s1.getASplit()) and - s1 != s2 - } - - query predicate breakInvariant2( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = predSplits.getASplit() and - split.hasSuccessor(pred, succ, c) and - not split = succSplits.getASplit() - } - - query predicate breakInvariant3( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = predSplits.getASplit() and - split.hasExit(pred, succ, c) and - not split.hasEntry(pred, succ, c) and - split = succSplits.getASplit() - } - - query predicate breakInvariant4( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split.hasEntry(pred, succ, c) and - not split.getKind() = predSplits.getASplit().getKind() and - not split = succSplits.getASplit() and - split.getKind().isEnabled(succ) - } - - query predicate breakInvariant5( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = succSplits.getASplit() and - not (split.hasSuccessor(pred, succ, c) and split = predSplits.getASplit()) and - not split.hasEntry(pred, succ, c) - } - - private class SimpleSuccessorType extends SuccessorType { - SimpleSuccessorType() { - this = getAMatchingSuccessorType(any(Completion c | completionIsSimple(c))) - } - } - - private class NormalSuccessorType extends SuccessorType { - NormalSuccessorType() { - this = getAMatchingSuccessorType(any(Completion c | completionIsNormal(c))) - } - } - - query predicate multipleSuccessors(Node node, SuccessorType t, Node successor) { - strictcount(getASuccessor(node, t)) > 1 and - successor = getASuccessor(node, t) and - // allow for functions with multiple bodies - not (t instanceof SimpleSuccessorType and node instanceof TEntryNode) - } - - query predicate simpleAndNormalSuccessors( - Node node, NormalSuccessorType t1, SimpleSuccessorType t2, Node succ1, Node succ2 - ) { - t1 != t2 and - succ1 = getASuccessor(node, t1) and - succ2 = getASuccessor(node, t2) - } - - query predicate deadEnd(Node node) { - not node instanceof TExitNode and - not exists(getASuccessor(node, _)) - } - - query predicate nonUniqueSplitKind(SplitImpl split, SplitKind sk) { - sk = split.getKind() and - strictcount(split.getKind()) > 1 - } - - query predicate nonUniqueListOrder(SplitKind sk, int ord) { - ord = sk.getListOrder() and - strictcount(sk.getListOrder()) > 1 - } -} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll deleted file mode 100644 index 1a1bf533100..00000000000 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll +++ /dev/null @@ -1,74 +0,0 @@ -private import codeql.ruby.AST as RB -private import ControlFlowGraphImpl as Impl -private import Completion as Comp -private import codeql.ruby.ast.internal.Synthesis -private import Splitting as Splitting -private import codeql.ruby.CFG as Cfg - -/** The base class for `ControlFlowTree`. */ -class ControlFlowTreeBase extends RB::AstNode { - ControlFlowTreeBase() { not any(Synthesis s).excludeFromControlFlowTree(this) } -} - -class ControlFlowElement = RB::AstNode; - -class Completion = Comp::Completion; - -/** - * Hold if `c` represents normal evaluation of a statement or an - * expression. - */ -predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion } - -/** - * Hold if `c` represents simple (normal) evaluation of a statement or an - * expression. - */ -predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion } - -/** Holds if `c` is a valid completion for `e`. */ -predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) } - -class CfgScope = Cfg::CfgScope; - -predicate getCfgScope = Impl::getCfgScope/1; - -/** Holds if `first` is first executed when entering `scope`. */ -predicate scopeFirst(CfgScope scope, ControlFlowElement first) { - scope.(Impl::CfgScopeImpl).entry(first) -} - -/** Holds if `scope` is exited when `last` finishes with completion `c`. */ -predicate scopeLast(CfgScope scope, ControlFlowElement last, Completion c) { - scope.(Impl::CfgScopeImpl).exit(last, c) -} - -/** The maximum number of splits allowed for a given node. */ -int maxSplits() { result = 5 } - -class SplitKindBase = Splitting::TSplitKind; - -class Split = Splitting::Split; - -class SuccessorType = Cfg::SuccessorType; - -/** Gets a successor type that matches completion `c`. */ -SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } - -/** - * Hold if `c` represents simple (normal) evaluation of a statement or an - * expression. - */ -predicate successorTypeIsSimple(SuccessorType t) { - t instanceof Cfg::SuccessorTypes::NormalSuccessor -} - -/** Holds if `t` is an abnormal exit type out of a CFG scope. */ -predicate isAbnormalExitType(SuccessorType t) { - t instanceof Cfg::SuccessorTypes::RaiseSuccessor or - t instanceof Cfg::SuccessorTypes::ExitSuccessor -} - -class Location = RB::Location; - -class Node = Cfg::CfgNode; diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll index a048e2d4044..e5b4036d054 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll @@ -2,7 +2,7 @@ * Provides classes and predicates relevant for splitting the control flow graph. */ -private import codeql.ruby.AST +private import codeql.ruby.AST as Ast private import Completion private import ControlFlowGraphImpl private import SuccessorTypes @@ -64,31 +64,36 @@ private module ConditionalCompletionSplitting { int getNextListOrder() { result = 1 } - private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit { + private class ConditionalCompletionSplitImpl extends SplitImpl instanceof ConditionalCompletionSplit + { + ConditionalCompletion completion; + + ConditionalCompletionSplitImpl() { this = TConditionalCompletionSplit(completion) } + override ConditionalCompletionSplitKind getKind() { any() } override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { succ(pred, succ, c) and last(succ, _, completion) and ( - last(succ.(NotExpr).getOperand(), pred, c) and + last(succ.(Ast::NotExpr).getOperand(), pred, c) and completion.(BooleanCompletion).getDual() = c or - last(succ.(LogicalAndExpr).getAnOperand(), pred, c) and + last(succ.(Ast::LogicalAndExpr).getAnOperand(), pred, c) and completion = c or - last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and + last(succ.(Ast::LogicalOrExpr).getAnOperand(), pred, c) and completion = c or - last(succ.(StmtSequence).getLastStmt(), pred, c) and + last(succ.(Ast::StmtSequence).getLastStmt(), pred, c) and completion = c or - last(succ.(ConditionalExpr).getBranch(_), pred, c) and + last(succ.(Ast::ConditionalExpr).getBranch(_), pred, c) and completion = c ) or succ(pred, succ, c) and - succ instanceof WhenClause and + succ instanceof Ast::WhenClause and completion = c } @@ -144,7 +149,7 @@ module EnsureSplitting { /** Holds if this node is the entry node in the `ensure` block it belongs to. */ predicate isEntryNode() { first(block.getEnsure(), this) } - BodyStmt getBlock() { result = block } + Ast::BodyStmt getBlock() { result = block } pragma[noinline] predicate isEntered(AstNode pred, int nestLevel, Completion c) { @@ -221,12 +226,12 @@ module EnsureSplitting { override string toString() { result = "ensure (" + nestLevel + ")" } } - private class EnsureSplitImpl extends SplitImpl, EnsureSplit { - override EnsureSplitKind getKind() { result.getNestLevel() = this.getNestLevel() } + private class EnsureSplitImpl extends SplitImpl instanceof EnsureSplit { + override EnsureSplitKind getKind() { result.getNestLevel() = super.getNestLevel() } override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { - succ.(EnsureNode).isEntered(pred, this.getNestLevel(), c) and - this.getType().isSplitForEntryCompletion(c) + succ.(EnsureNode).isEntered(pred, super.getNestLevel(), c) and + super.getType().isSplitForEntryCompletion(c) } override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } @@ -253,8 +258,8 @@ module EnsureSplitting { */ private predicate exit(AstNode pred, Completion c, boolean inherited) { exists(Trees::BodyStmtTree block, EnsureSplitType type | - this.exit0(pred, block, this.getNestLevel(), c) and - type = this.getType() + this.exit0(pred, block, super.getNestLevel(), c) and + type = super.getType() | if last(block.getEnsure(), pred, c) then @@ -302,9 +307,9 @@ module EnsureSplitting { // split must be able to exit with a `return` completion. this.appliesToPredecessor(pred) and exists(EnsureSplitImpl outer | - outer.getNestLevel() = this.getNestLevel() - 1 and + outer.(EnsureSplit).getNestLevel() = super.getNestLevel() - 1 and outer.exit(pred, c, inherited) and - this.getType() instanceof NormalSuccessor and + super.getType() instanceof NormalSuccessor and inherited = true ) } @@ -335,10 +340,10 @@ module EnsureSplitting { if en.isEntryNode() and en.getBlock() != pred.(EnsureNode).getBlock() then // entering a nested `ensure` block - en.getNestLevel() > this.getNestLevel() + en.getNestLevel() > super.getNestLevel() else // staying in the same (possibly nested) `ensure` block as `pred` - en.getNestLevel() >= this.getNestLevel() + en.getNestLevel() >= super.getNestLevel() ) } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index 7408412aa91..2521311c814 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -264,7 +264,7 @@ private predicate selfInToplevel(SelfVariable self, Module m) { private predicate asModulePattern(SsaDefinitionExtNode def, Module m) { exists(AsPattern ap | m = resolveConstantReadAccess(ap.getPattern()) and - def.getDefinitionExt().(Ssa::WriteDefinition).getWriteAccess().getNode() = + def.getDefinitionExt().(Ssa::WriteDefinition).getWriteAccess().getAstNode() = ap.getVariableAccess() ) } @@ -1416,6 +1416,8 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { * This is a temporary hook to support technical debt in the Go language; do not use. */ pragma[inline] -predicate golangSpecificParamArgFilter(DataFlowCall call, DataFlow::Node p, ArgumentNode arg) { +predicate golangSpecificParamArgFilter( + DataFlowCall call, DataFlow::ParameterNode p, ArgumentNode arg +) { any() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..277b639d0ab 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index b0de9745816..1975ac9781f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..05e0bc67b30 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll index b0de9745816..1975ac9781f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll index b0de9745816..1975ac9781f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll index e78a0814a14..7ee656da807 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll @@ -1,6 +1,9 @@ /** * Provides Ruby-specific definitions for use in the data flow library. */ + +private import codeql.dataflow.DataFlow + module Private { import DataFlowPrivate import DataFlowDispatch @@ -9,3 +12,16 @@ module Private { module Public { import DataFlowPublic } + +module RubyDataFlow implements InputSig { + import Private + import Public + + predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + Private::isParameterNode(p, c, pos) + } + + predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1; + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index f3249d377d8..70599e9a593 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -12,7 +12,7 @@ private import FlowSummaryImplSpecific as FlowSummaryImplSpecific private import codeql.ruby.frameworks.data.ModelsAsData /** Gets the callable in which this node occurs. */ -DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() } +DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) { @@ -118,7 +118,7 @@ module LocalFlow { /** Gets the SSA definition node corresponding to parameter `p`. */ SsaDefinitionExtNode getParameterDefNode(NamedParameter p) { exists(BasicBlock bb, int i | - bb.getNode(i).getNode() = p.getDefiningAccess() and + bb.getNode(i).getAstNode() = p.getDefiningAccess() and result.getDefinitionExt().definesAt(_, bb, i, _) ) } @@ -203,8 +203,8 @@ module LocalFlow { exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n | nodeFrom = n and exprTo = nodeTo.asExpr() and - n.getReturningNode().getNode() instanceof BreakStmt and - exprTo.getNode() instanceof Loop and + n.getReturningNode().getAstNode() instanceof BreakStmt and + exprTo.getAstNode() instanceof Loop and nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getReturningNode() ) or @@ -926,7 +926,7 @@ abstract class SourceReturnNode extends ReturnNode { private module ReturnNodes { private predicate isValid(CfgNodes::ReturningCfgNode node) { exists(ReturningStmt stmt, Callable scope | - stmt = node.getNode() and + stmt = node.getAstNode() and scope = node.getScope() | stmt instanceof ReturnStmt and @@ -952,7 +952,7 @@ private module ReturnNodes { } override ReturnKind getKindSource() { - if n.getNode() instanceof BreakStmt + if n.getAstNode() instanceof BreakStmt then result instanceof BreakReturnKind else exists(CfgScope scope | scope = this.getCfgScope() | @@ -1232,7 +1232,7 @@ class DataFlowType extends TDataFlowType { predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(NodeImpl n) { result = TTodoDataFlowType() and exists(n) } +DataFlowType getNodeType(Node n) { result = TTodoDataFlowType() and exists(n) } /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { none() } @@ -1304,8 +1304,6 @@ predicate neverSkipInPathGraph(Node n) { class DataFlowExpr = CfgNodes::ExprCfgNode; -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index 3b55e4f98b2..9502f04a7eb 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -1,7 +1,7 @@ private import codeql.ssa.Ssa as SsaImplCommon private import codeql.ruby.AST private import codeql.ruby.CFG as Cfg -private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared as ControlFlowGraphImplShared +private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.Variable private import Cfg::CfgNodes::ExprNodes @@ -31,7 +31,7 @@ private module SsaInput implements SsaImplCommon::InputSig { i = 0 or // ...or a class or module block. - bb.getNode(i).getNode() = scope.(ModuleBase).getAControlFlowEntryNode() and + bb.getNode(i).getAstNode() = scope.(ModuleBase).getAControlFlowEntryNode() and not scope instanceof Toplevel // handled by case above ) or @@ -124,10 +124,10 @@ private predicate capturedExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, Local private predicate namespaceSelfExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, SelfVariable v) { exists(Namespace ns, AstNode last | v.getDeclaringScope() = ns and - last = ControlFlowGraphImplShared::getAControlFlowExitNode(ns) and + last = ControlFlowGraphImpl::getAControlFlowExitNode(ns) and if last = ns - then bb.getNode(i).getAPredecessor().getNode() = last - else bb.getNode(i).getNode() = last + then bb.getNode(i).getAPredecessor().getAstNode() = last + else bb.getNode(i).getAstNode() = last ) } @@ -183,7 +183,7 @@ private predicate capturedCallRead(CallCfgNode call, Cfg::BasicBlock bb, int i, private predicate variableReadActual(Cfg::BasicBlock bb, int i, LocalVariable v) { exists(VariableReadAccess read | read.getVariable() = v and - read = bb.getNode(i).getNode() + read = bb.getNode(i).getAstNode() ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Ldap.qll b/ruby/ql/lib/codeql/ruby/frameworks/Ldap.qll new file mode 100644 index 00000000000..9f1de5116ed --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/Ldap.qll @@ -0,0 +1,70 @@ +/** + * Provides modeling for `net-ldap` a ruby library for LDAP. + */ + +private import ruby +private import codeql.ruby.ApiGraphs +private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.Concepts + +/** + * Provides modeling for `net-ldap` a ruby library for LDAP. + */ +module NetLdap { + /** + * Flow summary for `Net::LDAP.new`. This method establishes a connection to a LDAP server. + */ + private class LdapConnSummary extends SummarizedCallable { + LdapConnSummary() { this = "Net::LDAP.new" } + + override MethodCall getACall() { result = any(NetLdapConnection l).asExpr().getExpr() } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and output = "ReturnValue" and preservesValue = false + } + } + + /** + * Flow summary for `Net::LDAP.Filter`. + */ + private class LdapFilterSummary extends SummarizedCallable { + LdapFilterSummary() { this = "Net::LDAP::Filter" } + + override MethodCall getACall() { result = any(NetLdapFilter l).asExpr().getExpr() } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = ["Argument[0]", "Argument[1]"] and output = "ReturnValue" and preservesValue = false + } + } + + /** Net LDAP Api Node */ + private API::Node ldap() { result = API::getTopLevelMember("Net").getMember("LDAP") } + + /** A call that establishes a LDAP Connection */ + private class NetLdapConnection extends DataFlow::CallNode { + NetLdapConnection() { this in [ldap().getAnInstantiation(), ldap().getAMethodCall("open")] } + } + + /** A call that constructs a LDAP query */ + private class NetLdapFilter extends LdapConstruction::Range, DataFlow::CallNode { + NetLdapFilter() { + this = + any(ldap() + .getMember("Filter") + .getAMethodCall([ + "begins", "bineq", "contains", "ends", "eq", "equals", "ex", "ge", "le", "ne", + "present" + ]) + ) + } + + override DataFlow::Node getQuery() { result = this.getArgument([0, 1]) } + } + + /** A call considered as a LDAP execution. */ + private class NetLdapExecution extends LdapExecution::Range, DataFlow::CallNode { + NetLdapExecution() { this = any(NetLdapConnection l).getAMethodCall("search") } + + override DataFlow::Node getQuery() { result = this.getKeywordArgument(_) } + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 2e598711fcc..1cb4e189339 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -454,6 +454,14 @@ private API::Node getNodeFromPath(string type, AccessPath path, int n) { or // Apply a type step typeStep(getNodeFromPath(type, path, n), result) + or + // Apply a fuzzy step (without advancing 'n') + path.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromPath(type, path, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + path.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromPath(type, path, n - 1) } /** @@ -500,6 +508,14 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) // will themselves find by following type-steps. n > 0 and n < subPath.getNumToken() + or + // Apply a fuzzy step (without advancing 'n') + subPath.getToken(n).getName() = "Fuzzy" and + result = Specific::getAFuzzySuccessor(getNodeFromSubPath(base, subPath, n)) + or + // Skip a fuzzy step (advance 'n' without changing the current node) + subPath.getToken(n - 1).getName() = "Fuzzy" and + result = getNodeFromSubPath(base, subPath, n - 1) } /** @@ -561,7 +577,7 @@ private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) */ bindingset[name] private predicate isValidTokenNameInIdentifyingAccessPath(string name) { - name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"] + name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar", "Fuzzy"] or Specific::isExtraValidTokenNameInIdentifyingAccessPath(name) } @@ -572,7 +588,7 @@ private predicate isValidTokenNameInIdentifyingAccessPath(string name) { */ bindingset[name] private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) { - name = "ReturnValue" + name = ["ReturnValue", "Fuzzy"] or Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name) } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll index ed7a331c452..7247409612d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -176,6 +176,25 @@ API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) { ) } +pragma[inline] +API::Node getAFuzzySuccessor(API::Node node) { + result = node.getAMember() + or + result = node.getMethod(_) + or + result = + node.getArgumentAtPosition(any(DataFlowDispatch::ArgumentPosition apos | not apos.isSelf())) + or + result = + node.getParameterAtPosition(any(DataFlowDispatch::ParameterPosition ppos | not ppos.isSelf())) + or + result = node.getReturn() + or + result = node.getAnElement() + or + result = node.getInstance() +} + /** * Holds if `invoke` matches the Ruby-specific call site filter in `token`. */ diff --git a/ruby/ql/lib/codeql/ruby/security/HardcodedDataInterpretedAsCodeCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/HardcodedDataInterpretedAsCodeCustomizations.qll index 1cdf88169c3..0c1ce882a74 100644 --- a/ruby/ql/lib/codeql/ruby/security/HardcodedDataInterpretedAsCodeCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/HardcodedDataInterpretedAsCodeCustomizations.qll @@ -79,7 +79,7 @@ module HardcodedDataInterpretedAsCode { forex(StringComponentCfgNode c | c = this.asExpr().(ExprNodes::StringlikeLiteralCfgNode).getAComponent() | - c.getNode().(Ast::StringEscapeSequenceComponent).getRawText().matches("\\x%") + c.getAstNode().(Ast::StringEscapeSequenceComponent).getRawText().matches("\\x%") ) } } diff --git a/ruby/ql/lib/codeql/ruby/security/LdapInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/LdapInjectionCustomizations.qll new file mode 100644 index 00000000000..98687e2b57f --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/LdapInjectionCustomizations.qll @@ -0,0 +1,81 @@ +/** + * Provides default sources, sinks and sanitizers for detecting + * LDAP Injections, as well as extension points for adding your own + */ + +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.BarrierGuards +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.ApiGraphs + +/** + * Provides default sources, sinks and sanitizers for detecting + * LDAP Injections, as well as extension points for adding your own + */ +module LdapInjection { + /** A data flow source for LDAP Injection vulnerabilities */ + abstract class Source extends DataFlow::Node { } + + /** A data flow sink for LDAP Injection vulnerabilities */ + abstract class Sink extends DataFlow::Node { } + + /** A sanitizer for LDAP Injection vulnerabilities. */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * Additional taint steps for "LDAP Injection" vulnerabilities. + */ + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + attributeArrayTaintStep(nodeFrom, nodeTo) + } + + /** + * Additional taint step to handle elements inside an array, + * specifically in the context of the following LDAP search function: + * + * ldap.search(base: "", filter: "", attributes: [name]) + */ + private predicate attributeArrayTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(DataFlow::CallNode filterCall | + filterCall.getMethodName() = "[]" and + n1 = filterCall.getArgument(_) and + n2 = filterCall + ) + } + + /** + * A source of remote user input, considered as a flow source. + */ + private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * An LDAP query execution considered as a flow sink. + */ + private class LdapExecutionAsSink extends Sink { + LdapExecutionAsSink() { this = any(LdapExecution l).getQuery() } + } + + /** + * A comparison with a constant string, considered as a sanitizer-guard. + */ + private class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { } + + /** + * An inclusion check against an array of constant strings, considered as a + * sanitizer-guard. + */ + private class StringConstArrayInclusionCallAsSanitizer extends Sanitizer, + StringConstArrayInclusionCallBarrier + { } + + /** + * A call to `Net::LDAP::Filter.escape`, considered as a sanitizer. + */ + class NetLdapFilterEscapeSanitization extends Sanitizer { + NetLdapFilterEscapeSanitization() { + this = + API::getTopLevelMember("Net").getMember("LDAP").getMember("Filter").getAMethodCall("escape") + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/LdapInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/LdapInjectionQuery.qll new file mode 100644 index 00000000000..60fdc6b2db4 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/LdapInjectionQuery.qll @@ -0,0 +1,29 @@ +/** + * Provides default sources, sinks and sanitizers for detecting + * LDAP Injections, as well as extension points for adding your own + */ + +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking + +/** Provides a taint-tracking configuration for detecting LDAP Injections vulnerabilities. */ +module LdapInjection { + import LdapInjectionCustomizations::LdapInjection + + /** + * A taint-tracking configuration for detecting LDAP Injections vulnerabilities. + */ + private module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + LdapInjection::isAdditionalFlowStep(node1, node2) + } + } + + import TaintTracking::Global +} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll index d14c182d127..256d15212d0 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll @@ -230,7 +230,7 @@ predicate returnStep(Node nodeFrom, Node nodeTo) { // deliberately do not include `getInitializeTarget`, since calls to `new` should not // get the return value from `initialize`. Any fields being set in the initializer // will reach all reads via `callStep` and `localFieldStep`. - nodeTo.asExpr().getNode() = call.getNode() + nodeTo.asExpr().getAstNode() = call.getAstNode() ) } diff --git a/ruby/ql/lib/ide-contextual-queries/printCfg.ql b/ruby/ql/lib/ide-contextual-queries/printCfg.ql index 9c42fe91361..22681eb48f9 100644 --- a/ruby/ql/lib/ide-contextual-queries/printCfg.ql +++ b/ruby/ql/lib/ide-contextual-queries/printCfg.ql @@ -7,7 +7,7 @@ * @tags ide-contextual-queries/print-cfg */ -private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::TestOutput +private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::TestOutput private import codeql.IDEContextual /** diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 94c5477e126..6f6a06a1f31 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,11 +1,13 @@ name: codeql/ruby-all -version: 0.7.1 +version: 0.7.2 groups: ruby extractor: ruby dbscheme: ruby.dbscheme upgrades: upgrades library: true dependencies: + codeql/controlflow: ${workspace} + codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/ssa: ${workspace} diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index f7af0802f0a..4d7100e2c1d 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.7.2 + +### New Queries + +* Added a new experimental query, `rb/ldap-injection`, to detect cases where user input is incorporated into LDAP queries without proper validation or sanitization, potentially leading to LDAP injection vulnerabilities. + ## 0.7.1 ### New Queries diff --git a/ruby/ql/src/change-notes/released/0.7.2.md b/ruby/ql/src/change-notes/released/0.7.2.md new file mode 100644 index 00000000000..8741b19d2c5 --- /dev/null +++ b/ruby/ql/src/change-notes/released/0.7.2.md @@ -0,0 +1,5 @@ +## 0.7.2 + +### New Queries + +* Added a new experimental query, `rb/ldap-injection`, to detect cases where user input is incorporated into LDAP queries without proper validation or sanitization, potentially leading to LDAP injection vulnerabilities. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index e007a9aec3e..fee171e9685 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.1 +lastReleaseVersion: 0.7.2 diff --git a/ruby/ql/src/experimental/ldap-injection/LdapInjection.qhelp b/ruby/ql/src/experimental/ldap-injection/LdapInjection.qhelp new file mode 100644 index 00000000000..7fb715fdbf6 --- /dev/null +++ b/ruby/ql/src/experimental/ldap-injection/LdapInjection.qhelp @@ -0,0 +1,53 @@ + + + + +

    +If an LDAP query or DN is built using string concatenation or string formatting, and the +components of the concatenation include user input without any proper sanitization, a user +is likely to be able to run malicious LDAP queries. +

    +
    + + +

    +If user input must be included in an LDAP query or DN, it should be escaped to +avoid a malicious user providing special characters that change the meaning +of the query. +

    +
    + + +

    +In the following Rails example, an ActionController class +has a ldap_handler method to handle requests. +

    + +

    +The user and dc is specified using a parameter, user_name and dc provided by +the client which it then uses to build a LDAP query and DN. This value is accessible using the params method. +

    + +

    The first example uses the unsanitized user input directly +in the search filter and DN for the LDAP query. +A malicious user could provide special characters to change the meaning of these +components, and search for a completely different set of values.

    + + + +

    In the second example, the input provided by the user is sanitized before it is included in the search filter or DN. +This ensures the meaning of the query cannot be changed by a malicious user.

    + + +
    + + +
  • OWASP: LDAP Injection Prevention Cheat Sheet.
  • +
  • OWASP: LDAP Injection.
  • +
  • Wikipedia: LDAP injection.
  • +
  • BlackHat: LDAP Injection and Blind LDAP Injection.
  • +
  • LDAP: Understanding and Defending Against LDAP Injection Attacks.
  • +
    +
    diff --git a/ruby/ql/src/experimental/ldap-injection/LdapInjection.ql b/ruby/ql/src/experimental/ldap-injection/LdapInjection.ql new file mode 100644 index 00000000000..c12710128b5 --- /dev/null +++ b/ruby/ql/src/experimental/ldap-injection/LdapInjection.ql @@ -0,0 +1,21 @@ +/** + * @name LDAP Injection + * @description Building an LDAP query from user-controlled sources is vulnerable to insertion of + * malicious LDAP code by the user. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id rb/ldap-injection + * @tags security + * external/cwe/cwe-090 + */ + +import codeql.ruby.DataFlow +import codeql.ruby.security.LdapInjectionQuery +import LdapInjection::PathGraph + +from LdapInjection::PathNode source, LdapInjection::PathNode sink +where LdapInjection::flowPath(source, sink) +select sink.getNode(), source, sink, "This LDAP query depends on a $@.", source.getNode(), + "user-provided value" diff --git a/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionBad.rb b/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionBad.rb new file mode 100644 index 00000000000..4b833124b22 --- /dev/null +++ b/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionBad.rb @@ -0,0 +1,21 @@ +require 'net/ldap' + +class BadLdapController < ActionController::Base + def ldap_handler + name = params[:user_name] + dc = params[:dc] + ldap = Net::LDAP.new( + host: 'ldap.example.com', + port: 636, + encryption: :simple_tls, + auth: { + method: :simple, + username: 'uid=admin,dc=example,dc=com', + password: 'adminpassword' + } + ) + filter = Net::LDAP::Filter.eq('foo', name) + attrs = [name] + result = ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: filter, attributes: attrs) + end +end diff --git a/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionGood.rb b/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionGood.rb new file mode 100644 index 00000000000..0ac6b09aa75 --- /dev/null +++ b/ruby/ql/src/experimental/ldap-injection/examples/LdapInjectionGood.rb @@ -0,0 +1,27 @@ +require 'net/ldap' + +class GoodLdapController < ActionController::Base + def ldap_handler + name = params[:user_name] + ldap = Net::LDAP.new( + host: 'ldap.example.com', + port: 636, + encryption: :simple_tls, + auth: { + method: :simple, + username: 'uid=admin,dc=example,dc=com', + password: 'adminpassword' + } + ) + + name = if ["admin", "guest"].include? name + name + else + name = "none" + end + + filter = Net::LDAP::Filter.eq('foo', name) + attrs = ['dn'] + result = ldap.search(base: 'ou=people,dc=example,dc=com', filter: filter, attributes: attrs) + end +end diff --git a/ruby/ql/src/experimental/performance/UseDetect.ql b/ruby/ql/src/experimental/performance/UseDetect.ql index c58b544b976..56ab99ae405 100644 --- a/ruby/ql/src/experimental/performance/UseDetect.ql +++ b/ruby/ql/src/experimental/performance/UseDetect.ql @@ -42,12 +42,12 @@ class EndCall extends MethodCall { } Expr getUniqueRead(Expr e) { - forex(CfgNode eNode | eNode.getNode() = e | + forex(CfgNode eNode | eNode.getAstNode() = e | exists(Ssa::WriteDefinition def | def.assigns(eNode) and strictcount(def.getARead()) = 1 and not def = any(Ssa::PhiNode phi).getAnInput() and - def.getARead().getNode() = result + def.getARead().getAstNode() = result ) ) } diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 19997bb0fd3..0f5c23a39da 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.7.1 +version: 0.7.2 groups: - ruby - queries diff --git a/ruby/ql/src/queries/analysis/Definitions.ql b/ruby/ql/src/queries/analysis/Definitions.ql index 51931118912..a1a0d6b258b 100644 --- a/ruby/ql/src/queries/analysis/Definitions.ql +++ b/ruby/ql/src/queries/analysis/Definitions.ql @@ -48,7 +48,7 @@ newtype DefLoc = /** A local variable. */ LocalVariableLoc(VariableReadAccess read, VariableWriteAccess write) { exists(Ssa::WriteDefinition w | - write = w.getWriteAccess().getNode() and + write = w.getWriteAccess().getAstNode() and read = w.getARead().getExpr() and not read.isSynthesized() ) diff --git a/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql b/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql index f5c96299b0b..2cf36bdc3e5 100644 --- a/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql +++ b/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql @@ -24,5 +24,5 @@ from RelevantLocalVariableWriteAccess write, LocalVariable v where v = write.getVariable() and exists(write.getAControlFlowNode()) and - not exists(Ssa::WriteDefinition def | def.getWriteAccess().getNode() = write) + not exists(Ssa::WriteDefinition def | def.getWriteAccess().getAstNode() = write) select write, "This assignment to $@ is useless, since its value is never read.", v, v.getName() diff --git a/ruby/ql/src/queries/variables/UnusedParameter.ql b/ruby/ql/src/queries/variables/UnusedParameter.ql index e13c2bdc6f5..d212ee883f7 100644 --- a/ruby/ql/src/queries/variables/UnusedParameter.ql +++ b/ruby/ql/src/queries/variables/UnusedParameter.ql @@ -23,5 +23,6 @@ class RelevantParameterVariable extends LocalVariable { } from RelevantParameterVariable v -where not exists(Ssa::WriteDefinition def | def.getWriteAccess().getNode() = v.getDefiningAccess()) +where + not exists(Ssa::WriteDefinition def | def.getWriteAccess().getAstNode() = v.getDefiningAccess()) select v, "The parameter '" + v.getName() + "' is never used." diff --git a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected index b92a74018fe..525470654b6 100644 --- a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected +++ b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected @@ -2815,12 +2815,15 @@ | file://:0:0:0:0 | [summary param] position 0 in Hash[] | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in Hash[] | | file://:0:0:0:0 | [summary param] position 0 in Mysql2::Client.escape() | file://:0:0:0:0 | [summary] to write: ReturnValue in Mysql2::Client.escape() | | file://:0:0:0:0 | [summary param] position 0 in Mysql2::Client.new() | file://:0:0:0:0 | [summary] to write: ReturnValue in Mysql2::Client.new() | +| file://:0:0:0:0 | [summary param] position 0 in Net::LDAP.new | file://:0:0:0:0 | [summary] to write: ReturnValue in Net::LDAP.new | +| file://:0:0:0:0 | [summary param] position 0 in Net::LDAP::Filter | file://:0:0:0:0 | [summary] to write: ReturnValue in Net::LDAP::Filter | | file://:0:0:0:0 | [summary param] position 0 in PG.new() | file://:0:0:0:0 | [summary] to write: ReturnValue in PG.new() | | file://:0:0:0:0 | [summary param] position 0 in Rack::Utils.parse_query | file://:0:0:0:0 | [summary] to write: ReturnValue in Rack::Utils.parse_query | | file://:0:0:0:0 | [summary param] position 0 in SQLite3::Database.quote() | file://:0:0:0:0 | [summary] to write: ReturnValue in SQLite3::Database.quote() | | file://:0:0:0:0 | [summary param] position 0 in Sequel.connect | file://:0:0:0:0 | [summary] to write: ReturnValue in Sequel.connect | | file://:0:0:0:0 | [summary param] position 0 in String.try_convert | file://:0:0:0:0 | [summary] to write: ReturnValue in String.try_convert | | file://:0:0:0:0 | [summary param] position 0 in \| | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in \| | +| file://:0:0:0:0 | [summary param] position 1 in Net::LDAP::Filter | file://:0:0:0:0 | [summary] to write: ReturnValue in Net::LDAP::Filter | | file://:0:0:0:0 | [summary param] position 1.. in File.join | file://:0:0:0:0 | [summary] to write: ReturnValue in File.join | | file://:0:0:0:0 | [summary param] self in & | file://:0:0:0:0 | [summary] read: Argument[self].Element[any] in & | | file://:0:0:0:0 | [summary param] self in * | file://:0:0:0:0 | [summary] read: Argument[self].Element[any] in * | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected index 0597947595a..0c02c5b2004 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -45,6 +45,14 @@ edges | summaries.rb:1:1:1:7 | tainted | summaries.rb:147:16:147:22 | tainted | | summaries.rb:1:1:1:7 | tainted | summaries.rb:150:39:150:45 | tainted | | summaries.rb:1:1:1:7 | tainted | summaries.rb:150:39:150:45 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:154:20:154:26 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:154:20:154:26 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:155:28:155:34 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:155:28:155:34 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:156:27:156:33 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:156:27:156:33 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:158:15:158:21 | tainted | +| summaries.rb:1:1:1:7 | tainted | summaries.rb:158:15:158:21 | tainted | | summaries.rb:1:11:1:36 | call to identity | summaries.rb:1:1:1:7 | tainted | | summaries.rb:1:11:1:36 | call to identity | summaries.rb:1:1:1:7 | tainted | | summaries.rb:1:20:1:36 | call to source | summaries.rb:1:11:1:36 | call to identity | @@ -232,6 +240,9 @@ edges | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:145:26:145:32 | tainted | | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:147:16:147:22 | tainted | | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:150:39:150:45 | tainted | +| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:154:20:154:26 | tainted | +| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:155:28:155:34 | tainted | +| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:156:27:156:33 | tainted | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:16:122:22 | [post] tainted | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:25:122:25 | [post] y | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:33:122:33 | [post] z | @@ -475,6 +486,18 @@ nodes | summaries.rb:147:16:147:22 | tainted | semmle.label | tainted | | summaries.rb:150:39:150:45 | tainted | semmle.label | tainted | | summaries.rb:150:39:150:45 | tainted | semmle.label | tainted | +| summaries.rb:154:20:154:26 | tainted | semmle.label | tainted | +| summaries.rb:154:20:154:26 | tainted | semmle.label | tainted | +| summaries.rb:155:28:155:34 | tainted | semmle.label | tainted | +| summaries.rb:155:28:155:34 | tainted | semmle.label | tainted | +| summaries.rb:156:27:156:33 | tainted | semmle.label | tainted | +| summaries.rb:156:27:156:33 | tainted | semmle.label | tainted | +| summaries.rb:158:15:158:21 | tainted | semmle.label | tainted | +| summaries.rb:158:15:158:21 | tainted | semmle.label | tainted | +| summaries.rb:163:20:163:36 | call to source | semmle.label | call to source | +| summaries.rb:163:20:163:36 | call to source | semmle.label | call to source | +| summaries.rb:166:20:166:36 | call to source | semmle.label | call to source | +| summaries.rb:166:20:166:36 | call to source | semmle.label | call to source | subpaths invalidSpecComponent #select @@ -574,6 +597,18 @@ invalidSpecComponent | summaries.rb:147:16:147:22 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:147:16:147:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | | summaries.rb:150:39:150:45 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:150:39:150:45 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | | summaries.rb:150:39:150:45 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:150:39:150:45 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:154:20:154:26 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:154:20:154:26 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:154:20:154:26 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:154:20:154:26 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:155:28:155:34 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:155:28:155:34 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:155:28:155:34 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:155:28:155:34 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:156:27:156:33 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:156:27:156:33 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:156:27:156:33 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:156:27:156:33 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:158:15:158:21 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:158:15:158:21 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:158:15:158:21 | tainted | summaries.rb:1:20:1:36 | call to source | summaries.rb:158:15:158:21 | tainted | $@ | summaries.rb:1:20:1:36 | call to source | call to source | +| summaries.rb:163:20:163:36 | call to source | summaries.rb:163:20:163:36 | call to source | summaries.rb:163:20:163:36 | call to source | $@ | summaries.rb:163:20:163:36 | call to source | call to source | +| summaries.rb:163:20:163:36 | call to source | summaries.rb:163:20:163:36 | call to source | summaries.rb:163:20:163:36 | call to source | $@ | summaries.rb:163:20:163:36 | call to source | call to source | +| summaries.rb:166:20:166:36 | call to source | summaries.rb:166:20:166:36 | call to source | summaries.rb:166:20:166:36 | call to source | $@ | summaries.rb:166:20:166:36 | call to source | call to source | +| summaries.rb:166:20:166:36 | call to source | summaries.rb:166:20:166:36 | call to source | summaries.rb:166:20:166:36 | call to source | $@ | summaries.rb:166:20:166:36 | call to source | call to source | warning | CSV type row should have 3 columns but has 1: TooFewColumns | | CSV type row should have 3 columns but has 6: TooManyColumns;;Member[Foo].Instance;too;many;columns | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql index 11145ed991c..89dce373b32 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql @@ -145,6 +145,7 @@ private class SinkFromModel extends ModelInput::SinkModelCsv { "Foo!;Method[getSinks].ReturnValue.Element[any].Method[mySink].Argument[0];test-sink", // "Foo!;Method[arraySink].Argument[0].Element[any];test-sink", // "Foo!;Method[secondArrayElementIsSink].Argument[0].Element[1];test-sink", // + "FuzzyLib!;Fuzzy.Method[fuzzyCall].Argument[0];test-sink" ] } } diff --git a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb index cff6f00884e..12907abb0a3 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb +++ b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb @@ -150,3 +150,19 @@ Foo.secondArrayElementIsSink([tainted, "safe", "safe"]) Foo.secondArrayElementIsSink(["safe", tainted, "safe"]) # $ hasValueFlow=tainted Foo.secondArrayElementIsSink(["safe", "safe", tainted]) Foo.secondArrayElementIsSink([tainted] * 10) # $ MISSING: hasValueFlow=tainted + +FuzzyLib.fuzzyCall(tainted) # $ hasValueFlow=tainted +FuzzyLib.foo.bar.fuzzyCall(tainted) # $ hasValueFlow=tainted +FuzzyLib.foo[0].fuzzyCall(tainted) # $ hasValueFlow=tainted +FuzzyLib.foo do |x| + x.fuzzyCall(tainted) # $ hasValueFlow=tainted + x.otherCall(tainted) +end +class FuzzySub < FuzzyLib::Foo + def blah + self.fuzzyCall(source("tainted")) # $ hasValueFlow=tainted + end + def self.blah + self.fuzzyCall(source("tainted")) # $ hasValueFlow=tainted + end +end diff --git a/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb b/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb new file mode 100644 index 00000000000..966b26ef636 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/LdapInjection/LdapInjection.rb @@ -0,0 +1,66 @@ +class FooController < ActionController::Base + def some_request_handler + # A string tainted by user input is used directly as DN + # (i.e a remote flow source) + dc = params[:dc] + + # A string tainted by user input is used directly as search filter or attribute + # (i.e a remote flow source) + name = params[:user_name] + + # LDAP Connection + ldap = Net::LDAP.new( + host: 'ldap.example.com', + port: 636, + encryption: :simple_tls, + auth: { + method: :simple, + username: 'uid=admin,dc=example,dc=com', + password: 'adminpassword' + } + ) + + # BAD: user input is used as DN + # where dc is unsanitized + ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: "cn=George", attributes: [""]) + + # BAD: user input is used as search filter + # where name is unsanitized + ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) + + # BAD: user input is used as attribute + # where name is unsanitized + ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [name]) + + # BAD: user input is used as search filter + # where name is unsanitized + filter = Net::LDAP::Filter.eq('cn', name) + ldap.search(base: "ou=people,dc=example,dc=com", filter: filter, attributes: [""]) + + # GOOD: user input is not used in the LDAP query + result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [""]) + end +end + +class BarController < ApplicationController + def safe_paths + dc = params[:dc] + # GOOD: barrier guard prevents taint flow + if dc == "example" + base = "ou=people,dc=#{dc},dc=com" + else + base = "ou=people,dc=default,dc=com" + end + ldap.search(base: base, filter: "cn=George", attributes: [""]) + + + name = params[:user_name] + # GOOD: barrier guard prevents taint flow + name = if ["George", "Nicolas"].include? name + name + else + name = "Guest" + end + result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.expected b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.expected new file mode 100644 index 00000000000..2c2aa193d77 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.expected @@ -0,0 +1,34 @@ +edges +| LdapInjection.rb:5:5:5:6 | dc | LdapInjection.rb:25:23:25:49 | "ou=people,dc=#{...},dc=com" | +| LdapInjection.rb:5:10:5:15 | call to params | LdapInjection.rb:5:10:5:20 | ...[...] | +| LdapInjection.rb:5:10:5:20 | ...[...] | LdapInjection.rb:5:5:5:6 | dc | +| LdapInjection.rb:9:5:9:8 | name | LdapInjection.rb:29:62:29:73 | "cn=#{...}" | +| LdapInjection.rb:9:5:9:8 | name | LdapInjection.rb:33:88:33:91 | name | +| LdapInjection.rb:9:12:9:17 | call to params | LdapInjection.rb:9:12:9:29 | ...[...] | +| LdapInjection.rb:9:12:9:29 | ...[...] | LdapInjection.rb:9:5:9:8 | name | +| LdapInjection.rb:33:88:33:91 | name | LdapInjection.rb:33:87:33:92 | call to [] | +| LdapInjection.rb:33:88:33:91 | name | LdapInjection.rb:37:41:37:44 | name | +| LdapInjection.rb:37:5:37:10 | filter | LdapInjection.rb:38:62:38:67 | filter | +| LdapInjection.rb:37:14:37:45 | call to eq | LdapInjection.rb:37:5:37:10 | filter | +| LdapInjection.rb:37:41:37:44 | name | LdapInjection.rb:37:14:37:45 | call to eq | +nodes +| LdapInjection.rb:5:5:5:6 | dc | semmle.label | dc | +| LdapInjection.rb:5:10:5:15 | call to params | semmle.label | call to params | +| LdapInjection.rb:5:10:5:20 | ...[...] | semmle.label | ...[...] | +| LdapInjection.rb:9:5:9:8 | name | semmle.label | name | +| LdapInjection.rb:9:12:9:17 | call to params | semmle.label | call to params | +| LdapInjection.rb:9:12:9:29 | ...[...] | semmle.label | ...[...] | +| LdapInjection.rb:25:23:25:49 | "ou=people,dc=#{...},dc=com" | semmle.label | "ou=people,dc=#{...},dc=com" | +| LdapInjection.rb:29:62:29:73 | "cn=#{...}" | semmle.label | "cn=#{...}" | +| LdapInjection.rb:33:87:33:92 | call to [] | semmle.label | call to [] | +| LdapInjection.rb:33:88:33:91 | name | semmle.label | name | +| LdapInjection.rb:37:5:37:10 | filter | semmle.label | filter | +| LdapInjection.rb:37:14:37:45 | call to eq | semmle.label | call to eq | +| LdapInjection.rb:37:41:37:44 | name | semmle.label | name | +| LdapInjection.rb:38:62:38:67 | filter | semmle.label | filter | +subpaths +#select +| LdapInjection.rb:25:23:25:49 | "ou=people,dc=#{...},dc=com" | LdapInjection.rb:5:10:5:15 | call to params | LdapInjection.rb:25:23:25:49 | "ou=people,dc=#{...},dc=com" | This LDAP query depends on a $@. | LdapInjection.rb:5:10:5:15 | call to params | user-provided value | +| LdapInjection.rb:29:62:29:73 | "cn=#{...}" | LdapInjection.rb:9:12:9:17 | call to params | LdapInjection.rb:29:62:29:73 | "cn=#{...}" | This LDAP query depends on a $@. | LdapInjection.rb:9:12:9:17 | call to params | user-provided value | +| LdapInjection.rb:33:87:33:92 | call to [] | LdapInjection.rb:9:12:9:17 | call to params | LdapInjection.rb:33:87:33:92 | call to [] | This LDAP query depends on a $@. | LdapInjection.rb:9:12:9:17 | call to params | user-provided value | +| LdapInjection.rb:38:62:38:67 | filter | LdapInjection.rb:9:12:9:17 | call to params | LdapInjection.rb:38:62:38:67 | filter | This LDAP query depends on a $@. | LdapInjection.rb:9:12:9:17 | call to params | user-provided value | diff --git a/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref new file mode 100644 index 00000000000..7df75a91d96 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/LdapInjection/Ldapinjection.qlref @@ -0,0 +1 @@ +experimental/ldap-injection/LdapInjection.ql \ No newline at end of file diff --git a/shared/controlflow/CHANGELOG.md b/shared/controlflow/CHANGELOG.md new file mode 100644 index 00000000000..21f862198c9 --- /dev/null +++ b/shared/controlflow/CHANGELOG.md @@ -0,0 +1,5 @@ +## 0.0.1 + +### Major Analysis Improvements + +* Initial release. Adds a shared library for control flow analyses. diff --git a/shared/controlflow/change-notes/released/0.0.1.md b/shared/controlflow/change-notes/released/0.0.1.md new file mode 100644 index 00000000000..21f862198c9 --- /dev/null +++ b/shared/controlflow/change-notes/released/0.0.1.md @@ -0,0 +1,5 @@ +## 0.0.1 + +### Major Analysis Improvements + +* Initial release. Adds a shared library for control flow analyses. diff --git a/shared/controlflow/codeql-pack.release.yml b/shared/controlflow/codeql-pack.release.yml new file mode 100644 index 00000000000..c6933410b71 --- /dev/null +++ b/shared/controlflow/codeql-pack.release.yml @@ -0,0 +1,2 @@ +--- +lastReleaseVersion: 0.0.1 diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll new file mode 100644 index 00000000000..26429c1c7ab --- /dev/null +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -0,0 +1,1288 @@ +/** + * Provides a shared interface and implementation for constructing control-flow graphs + * (CFGs) from abstract syntax trees (ASTs). + */ + +private import codeql.util.Location + +/** Provides the language-specific input specification. */ +signature module InputSig { + /** An AST node. */ + class AstNode { + /** Gets a textual representation of this element. */ + string toString(); + + /** Gets the location of this element. */ + Location getLocation(); + } + + /** A control-flow completion. */ + class Completion { + /** Gets a textual representation of this completion. */ + string toString(); + } + + /** + * Hold if `c` represents normal evaluation of a statement or an + * expression. + */ + predicate completionIsNormal(Completion c); + + /** + * Hold if `c` represents simple (normal) evaluation of a statement or an + * expression. + */ + predicate completionIsSimple(Completion c); + + /** Holds if `c` is a valid completion for AST node `n`. */ + predicate completionIsValidFor(Completion c, AstNode n); + + /** A CFG scope. Each CFG scope gets its own control flow graph. */ + class CfgScope { + /** Gets a textual representation of this scope. */ + string toString(); + + /** Gets the location of this scope. */ + Location getLocation(); + } + + /** Gets the CFG scope of AST node `n`. */ + CfgScope getCfgScope(AstNode n); + + /** Holds if `first` is executed first when entering `scope`. */ + predicate scopeFirst(CfgScope scope, AstNode first); + + /** Holds if `scope` is exited when `last` finishes with completion `c`. */ + predicate scopeLast(CfgScope scope, AstNode last, Completion c); + + /** Gets the maximum number of splits allowed for a given node. */ + default int maxSplits() { result = 5 } + + /** The base class of `SplitKind`. */ + class SplitKindBase; + + /** A split. */ + class Split { + /** Gets a textual representation of this split. */ + string toString(); + } + + /** A type of a control flow successor. */ + class SuccessorType { + /** Gets a textual representation of this successor type. */ + string toString(); + } + + /** Gets a successor type that matches completion `c`. */ + SuccessorType getAMatchingSuccessorType(Completion c); + + /** + * Hold if `t` represents simple (normal) evaluation of a statement or an + * expression. + */ + predicate successorTypeIsSimple(SuccessorType t); + + /** Hold if `t` represents a conditional successor type. */ + predicate successorTypeIsCondition(SuccessorType t); + + /** Holds if `t` is an abnormal exit type out of a CFG scope. */ + predicate isAbnormalExitType(SuccessorType t); +} + +/** + * Provides a shared interface for constructing control-flow graphs (CFGs) from + * abstract syntax trees (ASTs). + * + * The implementation is centered around the concept of a _completion_, which + * models how the execution of a statement or expression terminates. + * + * The CFG is built by structural recursion over the AST, using the abstract class + * `ControlFlowTree`. To achieve this, the CFG edges related to a given AST node, + * `n`, are divided into three categories: + * + * 1. The in-going edge that points to the first CFG node to execute when + * `n` is going to be executed. + * 2. The out-going edges for control flow leaving `n` that are going to some + * other node in the surrounding context of `n`. + * 3. The edges that have both of their end-points entirely within the AST + * node and its children. + * + * The edges in (1) and (2) are inherently non-local and are therefore + * initially calculated as half-edges, that is, the single node, `k`, of the + * edge contained within `n`, by the predicates `k = n.first()` and `k = n.last(_)`, + * respectively. The edges in (3) can then be enumerated directly by the predicate + * `succ` by calling `first` and `last` recursively on the children of `n` and + * connecting the end-points. This yields the entire CFG, since all edges are in + * (3) for _some_ AST node. + * + * The parameter of `last` is the completion, which is necessary to distinguish + * the out-going edges from `n`. Note that the completion changes as the calculation of + * `last` proceeds outward through the AST; for example, a completion representing a + * loop break will be caught up by its surrounding loop and turned into a normal + * completion. + */ +module Make Input> { + private import Input + + final private class AstNodeFinal = AstNode; + + /** An element with associated control flow. */ + abstract class ControlFlowTree extends AstNodeFinal { + /** Holds if `first` is the first element executed within this element. */ + pragma[nomagic] + abstract predicate first(AstNode first); + + /** + * Holds if `last` with completion `c` is a potential last element executed + * within this element. + */ + pragma[nomagic] + abstract predicate last(AstNode last, Completion c); + + /** Holds if abnormal execution of `child` should propagate upwards. */ + abstract predicate propagatesAbnormal(AstNode child); + + /** + * Holds if `succ` is a control flow successor for `pred`, given that `pred` + * finishes with completion `c`. + */ + pragma[nomagic] + abstract predicate succ(AstNode pred, AstNode succ, Completion c); + } + + /** + * Holds if `first` is the first element executed within control flow + * element `cft`. + */ + predicate first(ControlFlowTree cft, AstNode first) { cft.first(first) } + + /** + * Holds if `last` with completion `c` is a potential last element executed + * within control flow element `cft`. + */ + predicate last(ControlFlowTree cft, AstNode last, Completion c) { + cft.last(last, c) + or + exists(AstNode n | + cft.propagatesAbnormal(n) and + last(n, last, c) and + not completionIsNormal(c) + ) + } + + /** + * Holds if `succ` is a control flow successor for `pred`, given that `pred` + * finishes with completion `c`. + */ + pragma[nomagic] + predicate succ(AstNode pred, AstNode succ, Completion c) { + any(ControlFlowTree cft).succ(pred, succ, c) + } + + /** An element that is executed in pre-order, typically used for statements. */ + abstract class PreOrderTree extends ControlFlowTree { + final override predicate first(AstNode first) { first = this } + } + + /** An element that is executed in post-order, typically used for expressions. */ + abstract class PostOrderTree extends ControlFlowTree { + override predicate last(AstNode last, Completion c) { + last = this and + completionIsValidFor(c, last) + } + } + + /** + * An element where the children are evaluated following a standard left-to-right + * evaluation. The actual evaluation order is determined by the predicate + * `getChildNode()`. + */ + abstract class StandardTree extends ControlFlowTree { + /** Gets the `i`th child element, in order of evaluation. */ + abstract AstNode getChildNode(int i); + + private AstNode getChildNodeRanked(int i) { + result = rank[i + 1](AstNode child, int j | child = this.getChildNode(j) | child order by j) + } + + /** Gets the first child node of this element. */ + final AstNode getFirstChildNode() { result = this.getChildNodeRanked(0) } + + /** Gets the last child node of this node. */ + final AstNode getLastChildElement() { + exists(int last | + result = this.getChildNodeRanked(last) and + not exists(this.getChildNodeRanked(last + 1)) + ) + } + + /** Holds if this element has no children. */ + predicate isLeafElement() { not exists(this.getFirstChildNode()) } + + override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) } + + pragma[nomagic] + override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(int i | + last(this.getChildNodeRanked(i), pred, c) and + completionIsNormal(c) and + first(this.getChildNodeRanked(i + 1), succ) + ) + } + } + + /** A standard element that is executed in pre-order. */ + abstract class StandardPreOrderTree extends StandardTree, PreOrderTree { + override predicate last(AstNode last, Completion c) { + last(this.getLastChildElement(), last, c) + or + this.isLeafElement() and + completionIsValidFor(c, this) and + last = this + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + StandardTree.super.succ(pred, succ, c) + or + pred = this and + first(this.getFirstChildNode(), succ) and + completionIsSimple(c) + } + } + + /** A standard element that is executed in post-order. */ + abstract class StandardPostOrderTree extends StandardTree, PostOrderTree { + override predicate first(AstNode first) { + first(this.getFirstChildNode(), first) + or + not exists(this.getFirstChildNode()) and + first = this + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + StandardTree.super.succ(pred, succ, c) + or + last(this.getLastChildElement(), pred, c) and + succ = this and + completionIsNormal(c) + } + } + + /** An element that is a leaf in the control flow graph. */ + abstract class LeafTree extends PreOrderTree, PostOrderTree { + override predicate propagatesAbnormal(AstNode child) { none() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } + } + + /** + * Holds if split kinds `sk1` and `sk2` may overlap. That is, they may apply + * to at least one common AST node inside `scope`. + */ + private predicate overlapping(CfgScope scope, SplitKind sk1, SplitKind sk2) { + exists(AstNode e | + sk1.appliesTo(e) and + sk2.appliesTo(e) and + scope = getCfgScope(e) + ) + } + + /** + * A split kind. Each control flow node can have at most one split of a + * given kind. + */ + abstract class SplitKind instanceof SplitKindBase { + /** Gets a split of this kind. */ + SplitImpl getASplit() { result.getKind() = this } + + /** Holds if some split of this kind applies to AST node `n`. */ + predicate appliesTo(AstNode n) { this.getASplit().appliesTo(n) } + + /** + * Gets a unique integer representing this split kind. The integer is used + * to represent sets of splits as ordered lists. + */ + abstract int getListOrder(); + + /** Gets the rank of this split kind among all overlapping kinds for `c`. */ + private int getRank(CfgScope scope) { + this = + rank[result](SplitKind sk | overlapping(scope, this, sk) | sk order by sk.getListOrder()) + } + + /** + * Holds if this split kind is enabled for AST node `n`. For performance reasons, + * the number of splits is restricted by the `maxSplits()` predicate. + */ + predicate isEnabled(AstNode n) { + this.appliesTo(n) and + this.getRank(getCfgScope(n)) <= maxSplits() + } + + /** + * Gets the rank of this split kind among all the split kinds that apply to + * AST node `n`. The rank is based on the order defined by `getListOrder()`. + */ + int getListRank(AstNode n) { + this.isEnabled(n) and + this = rank[result](SplitKind sk | sk.appliesTo(n) | sk order by sk.getListOrder()) + } + + /** Gets a textual representation of this split kind. */ + abstract string toString(); + } + + final private class SplitFinal = Split; + + /** An interface for implementing an entity to split on. */ + abstract class SplitImpl extends SplitFinal { + /** Gets the kind of this split. */ + abstract SplitKind getKind(); + + /** + * Holds if this split is entered when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasEntry(pred, succ, c) implies succ(pred, succ, c)`. + */ + abstract predicate hasEntry(AstNode pred, AstNode succ, Completion c); + + /** + * Holds if this split is entered when control passes from `scope` to the entry point + * `first`. + * + * Invariant: `hasEntryScope(scope, first) implies scopeFirst(scope, first)`. + */ + abstract predicate hasEntryScope(CfgScope scope, AstNode first); + + /** + * Holds if this split is left when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasExit(pred, succ, c) implies succ(pred, succ, c)`. + */ + abstract predicate hasExit(AstNode pred, AstNode succ, Completion c); + + /** + * Holds if this split is left when control passes from `last` out of the enclosing + * scope `scope` with completion `c`. + * + * Invariant: `hasExitScope(scope, last, c) implies scopeLast(scope, last, c)` + */ + abstract predicate hasExitScope(CfgScope scope, AstNode last, Completion c); + + /** + * Holds if this split is maintained when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasSuccessor(pred, succ, c) implies succ(pred, succ, c)` + */ + abstract predicate hasSuccessor(AstNode pred, AstNode succ, Completion c); + + /** Holds if this split applies to AST node `n`. */ + final predicate appliesTo(AstNode n) { + this.hasEntry(_, n, _) + or + this.hasEntryScope(_, n) + or + exists(AstNode pred | this.appliesTo(pred) | this.hasSuccessor(pred, n, _)) + } + + /** + * Holds if `succ` is a control flow successor for `pred`, given that `pred` + * finishes with completion `c`, and this split applies to `pred`. + */ + pragma[noinline] + final predicate appliesSucc(AstNode pred, AstNode succ, Completion c) { + this.appliesTo(pred) and + succ(pred, succ, c) + } + } + + /** + * A set of control flow node splits. The set is represented by a list of splits, + * ordered by ascending rank. + */ + class Splits extends TSplits { + /** Gets a textual representation of this set of splits. */ + string toString() { result = splitsToString(this) } + + /** Gets a split belonging to this set of splits. */ + SplitImpl getASplit() { + exists(SplitImpl head, Splits tail | this = TSplitsCons(head, tail) | + result = head + or + result = tail.getASplit() + ) + } + } + + private predicate succEntrySplitsFromRank(CfgScope pred, AstNode succ, Splits splits, int rnk) { + splits = TSplitsNil() and + scopeFirst(pred, succ) and + rnk = 0 + or + exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | + splits = TSplitsCons(head, tail) + ) + } + + private predicate succEntrySplitsCons( + CfgScope pred, AstNode succ, SplitImpl head, Splits tail, int rnk + ) { + succEntrySplitsFromRank(pred, succ, tail, rnk - 1) and + head.hasEntryScope(pred, succ) and + rnk = head.getKind().getListRank(succ) + } + + /** + * Holds if `succ` with splits `succSplits` is the first element that is executed + * when entering callable `pred`. + */ + pragma[noinline] + private predicate succEntrySplits(CfgScope pred, AstNode succ, Splits succSplits, SuccessorType t) { + exists(int rnk | + scopeFirst(pred, succ) and + successorTypeIsSimple(t) and + succEntrySplitsFromRank(pred, succ, succSplits, rnk) + | + rnk = 0 and + not any(SplitImpl split).hasEntryScope(pred, succ) + or + rnk = + max(SplitImpl split | split.hasEntryScope(pred, succ) | split.getKind().getListRank(succ)) + ) + } + + /** + * Holds if `pred` with splits `predSplits` can exit the enclosing callable + * `succ` with type `t`. + */ + private predicate succExitSplits(AstNode pred, Splits predSplits, CfgScope succ, SuccessorType t) { + exists(Reachability::SameSplitsBlock b, Completion c | pred = b.getAnElement() | + b.isReachable(succ, predSplits) and + t = getAMatchingSuccessorType(c) and + scopeLast(succ, pred, c) and + forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | + predSplit.hasExitScope(succ, pred, c) + ) + ) + } + + /** + * Provides a predicate for the successor relation with split information, + * as well as logic used to construct the type `TSplits` representing sets + * of splits. Only sets of splits that can be reached are constructed, hence + * the predicates are mutually recursive. + * + * For the successor relation + * + * ```ql + * succSplits(AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, Completion c) + * ``` + * + * the following invariants are maintained: + * + * 1. `pred` is reachable with split set `predSplits`. + * 2. For all `split` in `predSplits`: + * - If `split.hasSuccessor(pred, succ, c)` then `split` in `succSplits`. + * 3. For all `split` in `predSplits`: + * - If `split.hasExit(pred, succ, c)` and not `split.hasEntry(pred, succ, c)` then + * `split` not in `succSplits`. + * 4. For all `split` with kind not in `predSplits`: + * - If `split.hasEntry(pred, succ, c)` then `split` in `succSplits`. + * 5. For all `split` in `succSplits`: + * - `split.hasSuccessor(pred, succ, c)` and `split` in `predSplits`, or + * - `split.hasEntry(pred, succ, c)`. + * + * The algorithm divides into four cases: + * + * 1. The set of splits for the successor is the same as the set of splits + * for the predecessor: + * a) The successor is in the same `SameSplitsBlock` as the predecessor. + * b) The successor is *not* in the same `SameSplitsBlock` as the predecessor. + * 2. The set of splits for the successor is different from the set of splits + * for the predecessor: + * a) The set of splits for the successor is *maybe* non-empty. + * b) The set of splits for the successor is *always* empty. + * + * Only case 2a may introduce new sets of splits, so only predicates from + * this case are used in the definition of `TSplits`. + * + * The predicates in this module are named after the cases above. + */ + private module SuccSplits { + private predicate succInvariant1( + Reachability::SameSplitsBlock b, AstNode pred, Splits predSplits, AstNode succ, Completion c + ) { + pred = b.getAnElement() and + b.isReachable(_, predSplits) and + succ(pred, succ, c) + } + + private predicate case1b0(AstNode pred, Splits predSplits, AstNode succ, Completion c) { + exists(Reachability::SameSplitsBlock b | + // Invariant 1 + succInvariant1(b, pred, predSplits, succ, c) + | + (succ = b.getAnElement() implies succ = b) and + // Invariant 4 + not exists(SplitImpl split | split.hasEntry(pred, succ, c)) + ) + } + + /** + * Case 1b. + * + * Invariants 1 and 4 hold in the base case, and invariants 2, 3, and 5 are + * maintained for all splits in `predSplits` (= `succSplits`), except + * possibly for the splits in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case1bForall( + AstNode pred, Splits predSplits, AstNode succ, Completion c, Splits except + ) { + case1b0(pred, predSplits, succ, c) and + except = predSplits + or + exists(SplitImpl split | + case1bForallCons(pred, predSplits, succ, c, split, except) and + split.hasSuccessor(pred, succ, c) + ) + } + + pragma[noinline] + private predicate case1bForallCons( + AstNode pred, Splits predSplits, AstNode succ, Completion c, SplitImpl exceptHead, + Splits exceptTail + ) { + case1bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) + } + + private predicate case1(AstNode pred, Splits predSplits, AstNode succ, Completion c) { + // Case 1a + exists(Reachability::SameSplitsBlock b | succInvariant1(b, pred, predSplits, succ, c) | + succ = b.getAnElement() and + not succ = b + ) + or + // Case 1b + case1bForall(pred, predSplits, succ, c, TSplitsNil()) + } + + pragma[noinline] + private SplitImpl succInvariant1GetASplit( + Reachability::SameSplitsBlock b, AstNode pred, Splits predSplits, AstNode succ, Completion c + ) { + succInvariant1(b, pred, predSplits, succ, c) and + result = predSplits.getASplit() + } + + private predicate case2aux(AstNode pred, Splits predSplits, AstNode succ, Completion c) { + exists(Reachability::SameSplitsBlock b | + succInvariant1(b, pred, predSplits, succ, c) and + (succ = b.getAnElement() implies succ = b) + | + succInvariant1GetASplit(b, pred, predSplits, succ, c).hasExit(pred, succ, c) + or + any(SplitImpl split).hasEntry(pred, succ, c) + ) + } + + /** + * Holds if `succSplits` should not inherit a split of kind `sk` from + * `predSplits`, except possibly because of a split in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case2aNoneInheritedOfKindForall( + AstNode pred, Splits predSplits, AstNode succ, Completion c, SplitKind sk, Splits except + ) { + case2aux(pred, predSplits, succ, c) and + sk.appliesTo(succ) and + except = predSplits + or + exists(Splits mid, SplitImpl split | + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, mid) and + mid = TSplitsCons(split, except) + | + split.getKind() = any(SplitKind sk0 | sk0 != sk and sk0.appliesTo(succ)) + or + split.hasExit(pred, succ, c) + ) + } + + pragma[nomagic] + private predicate entryOfKind( + AstNode pred, AstNode succ, Completion c, SplitImpl split, SplitKind sk + ) { + split.hasEntry(pred, succ, c) and + sk = split.getKind() + } + + /** Holds if `succSplits` should not have a split of kind `sk`. */ + pragma[nomagic] + private predicate case2aNoneOfKind( + AstNode pred, Splits predSplits, AstNode succ, Completion c, SplitKind sk + ) { + // None inherited from predecessor + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and + // None newly entered into + not entryOfKind(pred, succ, c, _, sk) + } + + /** Holds if `succSplits` should not have a split of kind `sk` at rank `rnk`. */ + pragma[nomagic] + private predicate case2aNoneAtRank( + AstNode pred, Splits predSplits, AstNode succ, Completion c, int rnk + ) { + exists(SplitKind sk | case2aNoneOfKind(pred, predSplits, succ, c, sk) | + rnk = sk.getListRank(succ) + ) + } + + pragma[nomagic] + private SplitImpl case2auxGetAPredecessorSplit( + AstNode pred, Splits predSplits, AstNode succ, Completion c + ) { + case2aux(pred, predSplits, succ, c) and + result = predSplits.getASplit() + } + + /** Gets a split that should be in `succSplits`. */ + pragma[nomagic] + private SplitImpl case2aSome( + AstNode pred, Splits predSplits, AstNode succ, Completion c, SplitKind sk + ) { + ( + // Inherited from predecessor + result = case2auxGetAPredecessorSplit(pred, predSplits, succ, c) and + result.hasSuccessor(pred, succ, c) + or + // Newly entered into + exists(SplitKind sk0 | + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk0, TSplitsNil()) + | + entryOfKind(pred, succ, c, result, sk0) + ) + ) and + sk = result.getKind() + } + + /** Gets a split that should be in `succSplits` at rank `rnk`. */ + pragma[nomagic] + SplitImpl case2aSomeAtRank(AstNode pred, Splits predSplits, AstNode succ, Completion c, int rnk) { + exists(SplitKind sk | result = case2aSome(pred, predSplits, succ, c, sk) | + rnk = sk.getListRank(succ) + ) + } + + /** + * Case 2a. + * + * As opposed to the other cases, in this case we need to construct a new set + * of splits `succSplits`. Since this involves constructing the very IPA type, + * we cannot recurse directly over the structure of `succSplits`. Instead, we + * recurse over the ranks of all splits that *might* be in `succSplits`. + * + * - Invariant 1 holds in the base case, + * - invariant 2 holds for all splits with rank at least `rnk`, + * - invariant 3 holds for all splits in `predSplits`, + * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`, + * and + * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`. + */ + predicate case2aFromRank( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, Completion c, int rnk + ) { + case2aux(pred, predSplits, succ, c) and + succSplits = TSplitsNil() and + rnk = max(any(SplitKind sk).getListRank(succ)) + 1 + or + case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and + case2aNoneAtRank(pred, predSplits, succ, c, rnk) + or + exists(Splits mid, SplitImpl split | split = case2aCons(pred, predSplits, succ, mid, c, rnk) | + succSplits = TSplitsCons(split, mid) + ) + } + + pragma[noinline] + private SplitImpl case2aCons( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, Completion c, int rnk + ) { + case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and + result = case2aSomeAtRank(pred, predSplits, succ, c, rnk) + } + + /** + * Case 2b. + * + * Invariants 1, 4, and 5 hold in the base case, and invariants 2 and 3 are + * maintained for all splits in `predSplits`, except possibly for the splits + * in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case2bForall( + AstNode pred, Splits predSplits, AstNode succ, Completion c, Splits except + ) { + // Invariant 1 + case2aux(pred, predSplits, succ, c) and + // Invariants 4 and 5 + not any(SplitKind sk).appliesTo(succ) and + except = predSplits + or + exists(SplitImpl split | case2bForallCons(pred, predSplits, succ, c, split, except) | + // Invariants 2 and 3 + split.hasExit(pred, succ, c) + ) + } + + pragma[noinline] + private predicate case2bForallCons( + AstNode pred, Splits predSplits, AstNode succ, Completion c, SplitImpl exceptHead, + Splits exceptTail + ) { + case2bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) + } + + private predicate case2( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, Completion c + ) { + case2aFromRank(pred, predSplits, succ, succSplits, c, 1) + or + case2bForall(pred, predSplits, succ, c, TSplitsNil()) and + succSplits = TSplitsNil() + } + + /** + * Holds if `succ` with splits `succSplits` is a successor of type `t` for `pred` + * with splits `predSplits`. + */ + predicate succSplits( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, Completion c + ) { + case1(pred, predSplits, succ, c) and + succSplits = predSplits + or + case2(pred, predSplits, succ, succSplits, c) + } + } + + private import SuccSplits + + /** Provides logic for calculating reachable control flow nodes. */ + private module Reachability { + /** + * Holds if `n` is an AST node where the set of possible splits may + * be different from the set of possible splits for one of `n`'s predecessors. + * That is, `n` starts a new block of elements with the same set of splits. + */ + private predicate startsSplits(AstNode n) { + scopeFirst(_, n) + or + exists(SplitImpl s | + s.hasEntry(_, n, _) + or + s.hasExit(_, n, _) + ) + or + exists(AstNode pred, SplitImpl split, Completion c | succ(pred, n, c) | + split.appliesTo(pred) and + not split.hasSuccessor(pred, n, c) + ) + } + + private predicate intraSplitsSucc(AstNode pred, AstNode succ) { + succ(pred, succ, _) and + not startsSplits(succ) + } + + private predicate splitsBlockContains(AstNode start, AstNode n) = + fastTC(intraSplitsSucc/2)(start, n) + + /** + * A block of control flow elements where the set of splits is guaranteed + * to remain unchanged, represented by the first element in the block. + */ + class SameSplitsBlock extends AstNodeFinal { + SameSplitsBlock() { startsSplits(this) } + + /** Gets a control flow element in this block. */ + AstNode getAnElement() { + splitsBlockContains(this, result) + or + result = this + } + + pragma[noinline] + private SameSplitsBlock getASuccessor(Splits predSplits, Splits succSplits) { + exists(AstNode pred | pred = this.getAnElement() | + succSplits(pred, predSplits, result, succSplits, _) + ) + } + + /** + * Holds if the elements of this block are reachable from a callable entry + * point, with the splits `splits`. + */ + predicate isReachable(CfgScope scope, Splits splits) { + // Base case + succEntrySplits(scope, this, splits, _) + or + // Recursive case + exists(SameSplitsBlock pred, Splits predSplits | pred.isReachable(scope, predSplits) | + this = pred.getASuccessor(predSplits, splits) + ) + } + } + } + + cached + private module Cached { + cached + newtype TSplits = + TSplitsNil() or + TSplitsCons(SplitImpl head, Splits tail) { + exists(AstNode pred, Splits predSplits, AstNode succ, Completion c, int rnk | + case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) and + head = case2aSomeAtRank(pred, predSplits, succ, c, rnk) + ) + or + succEntrySplitsCons(_, _, head, tail, _) + } + + cached + string splitsToString(Splits splits) { + splits = TSplitsNil() and + result = "" + or + exists(SplitImpl head, Splits tail, string headString, string tailString | + splits = TSplitsCons(head, tail) + | + headString = head.toString() and + tailString = tail.toString() and + if tailString = "" + then result = headString + else + if headString = "" + then result = tailString + else result = headString + ", " + tailString + ) + } + + /** + * Internal representation of control flow nodes in the control flow graph. + * The control flow graph is pruned for unreachable nodes. + */ + cached + newtype TNode = + TEntryNode(CfgScope scope) { succEntrySplits(scope, _, _, _) } or + TAnnotatedExitNode(CfgScope scope, boolean normal) { + exists(Reachability::SameSplitsBlock b, SuccessorType t | b.isReachable(scope, _) | + succExitSplits(b.getAnElement(), _, scope, t) and + if isAbnormalExitType(t) then normal = false else normal = true + ) + } or + TExitNode(CfgScope scope) { + exists(Reachability::SameSplitsBlock b | b.isReachable(scope, _) | + succExitSplits(b.getAnElement(), _, scope, _) + ) + } or + TAstNode(CfgScope scope, AstNode n, Splits splits) { + exists(Reachability::SameSplitsBlock b | b.isReachable(scope, splits) | + n = b.getAnElement() + ) + } + + /** Gets a successor node of a given flow type, if any. */ + cached + Node getASuccessor(Node pred, SuccessorType t) { + // Callable entry node -> callable body + exists(AstNode succElement, Splits succSplits, CfgScope scope | + result = TAstNode(scope, succElement, succSplits) and + pred = TEntryNode(scope) and + succEntrySplits(scope, succElement, succSplits, t) + ) + or + exists(CfgScope scope, AstNode predElement, Splits predSplits | + pred = TAstNode(pragma[only_bind_into](scope), predElement, predSplits) + | + // Element node -> callable exit (annotated) + exists(boolean normal | + result = TAnnotatedExitNode(pragma[only_bind_into](scope), normal) and + succExitSplits(predElement, predSplits, scope, t) and + if isAbnormalExitType(t) then normal = false else normal = true + ) + or + // Element node -> element node + exists(AstNode succElement, Splits succSplits, Completion c | + result = TAstNode(pragma[only_bind_into](scope), succElement, succSplits) + | + succSplits(predElement, predSplits, succElement, succSplits, c) and + t = getAMatchingSuccessorType(c) + ) + ) + or + // Callable exit (annotated) -> callable exit + exists(CfgScope scope | + pred = TAnnotatedExitNode(scope, _) and + result = TExitNode(scope) and + successorTypeIsSimple(t) + ) + } + + /** + * Gets the CFG scope of node `n`. Unlike `getCfgScope`, this predicate + * is calculated based on reachability from an entry node, and it may + * yield different results for AST elements that are split into multiple + * scopes. + */ + cached + CfgScope getNodeCfgScope(Node n) { + n = TEntryNode(result) + or + n = TAnnotatedExitNode(result, _) + or + n = TExitNode(result) + or + n = TAstNode(result, _, _) + } + + cached + module Public { + /** + * If needed, call this predicate from in order to force a stage-dependency on this + * cached stage. + */ + cached + predicate forceCachingInSameStage() { any() } + + /** + * Gets a first AST node executed within `n`. + */ + cached + AstNode getAControlFlowEntryNode(AstNode n) { first(n, result) } + + /** + * Gets a potential last AST node executed within `n`. + */ + cached + AstNode getAControlFlowExitNode(AstNode n) { last(n, result, _) } + } + } + + private import Cached + import Cached::Public + + /** + * A control flow node. + * + * A control flow node is a node in the control flow graph (CFG). There is a + * many-to-one relationship between CFG nodes and AST nodes. + * + * Only nodes that can be reached from an entry point are included in the CFG. + */ + abstract private class NodeImpl extends TNode { + /** Gets a textual representation of this control flow node. */ + abstract string toString(); + + /** Gets the AST node that this node corresponds to, if any. */ + abstract AstNode getAstNode(); + + /** Gets the location of this control flow node. */ + abstract Location getLocation(); + + /** Holds if this control flow node has conditional successors. */ + predicate isCondition() { + exists(this.getASuccessor(any(SuccessorType t | successorTypeIsCondition(t)))) + } + + /** Gets the scope of this node. */ + CfgScope getScope() { result = getNodeCfgScope(this) } + + /** Gets a successor node of a given type, if any. */ + Node getASuccessor(SuccessorType t) { result = getASuccessor(this, t) } + + /** Gets an immediate successor, if any. */ + Node getASuccessor() { result = this.getASuccessor(_) } + + /** Gets an immediate predecessor node of a given flow type, if any. */ + Node getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** Gets an immediate predecessor, if any. */ + Node getAPredecessor() { result = this.getAPredecessor(_) } + + /** Holds if this node has more than one predecessor. */ + predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } + + /** Holds if this node has more than one successor. */ + predicate isBranch() { strictcount(this.getASuccessor()) > 1 } + } + + final class Node = NodeImpl; + + /** An entry node for a given scope. */ + private class EntryNodeImpl extends NodeImpl, TEntryNode { + private CfgScope scope; + + EntryNodeImpl() { this = TEntryNode(scope) } + + final override AstNode getAstNode() { none() } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { result = "enter " + scope } + } + + final class EntryNode = EntryNodeImpl; + + /** An exit node for a given scope, annotated with the type of exit. */ + private class AnnotatedExitNodeImpl extends NodeImpl, TAnnotatedExitNode { + private CfgScope scope; + private boolean normal; + + AnnotatedExitNodeImpl() { this = TAnnotatedExitNode(scope, normal) } + + /** Holds if this node represent a normal exit. */ + final predicate isNormal() { normal = true } + + final override AstNode getAstNode() { none() } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { + exists(string s | + normal = true and s = "normal" + or + normal = false and s = "abnormal" + | + result = "exit " + scope + " (" + s + ")" + ) + } + } + + final class AnnotatedExitNode = AnnotatedExitNodeImpl; + + /** An exit node for a given scope. */ + private class ExitNodeImpl extends NodeImpl, TExitNode { + private CfgScope scope; + + ExitNodeImpl() { this = TExitNode(scope) } + + final override AstNode getAstNode() { none() } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { result = "exit " + scope } + } + + final class ExitNode = ExitNodeImpl; + + /** + * A node for an AST node. + * + * Each AST node maps to zero or more `AstCfgNode`s: zero when the node is unreachable + * (dead) code or not important for control flow, and multiple when there are different + * splits for the AST node. + */ + private class AstCfgNodeImpl extends NodeImpl, TAstNode { + private Splits splits; + private AstNode n; + + AstCfgNodeImpl() { this = TAstNode(_, n, splits) } + + final override AstNode getAstNode() { result = n } + + override Location getLocation() { result = n.getLocation() } + + final override string toString() { + exists(string s | s = n.toString() | + result = "[" + this.getSplitsString() + "] " + s + or + not exists(this.getSplitsString()) and result = s + ) + } + + /** Gets a comma-separated list of strings for each split in this node, if any. */ + final string getSplitsString() { + result = splits.toString() and + result != "" + } + + /** Gets a split for this control flow node, if any. */ + final Split getASplit() { result = splits.getASplit() } + } + + final class AstCfgNode = AstCfgNodeImpl; + + /** + * Import this module into a `.ql` file of `@kind graph` to render a CFG. The + * graph is restricted to nodes from `RelevantNode`. + */ + module TestOutput { + /** A CFG node to include in the output. */ + abstract class RelevantNode extends Node { + /** + * Gets a string used to resolve ties in node and edge ordering. + */ + string getOrderDisambiguation() { result = "" } + } + + /** Holds if `n` is a relevant node in the CFG. */ + query predicate nodes(RelevantNode n, string attr, string val) { + attr = "semmle.order" and + val = + any(int i | + n = + rank[i](RelevantNode p, string filePath, int startLine, int startColumn, int endLine, + int endColumn | + p.getLocation().hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn) + | + p + order by + filePath, startLine, startColumn, endLine, endColumn, p.toString(), + p.getOrderDisambiguation() + ) + ).toString() + } + + /** Holds if `pred -> succ` is an edge in the CFG. */ + query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) { + attr = "semmle.label" and + val = + strictconcat(SuccessorType t, string s | + succ = getASuccessor(pred, t) and + if successorTypeIsSimple(t) then s = "" else s = t.toString() + | + s, ", " order by s + ) + or + attr = "semmle.order" and + val = + any(int i | + succ = + rank[i](RelevantNode s, SuccessorType t, string filePath, int startLine, + int startColumn, int endLine, int endColumn | + s = getASuccessor(pred, t) and + s.getLocation().hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn) + | + s + order by + filePath, startLine, startColumn, endLine, endColumn, t.toString(), s.toString(), + s.getOrderDisambiguation() + ) + ).toString() + } + } + + /** Provides a set of consistency queries. */ + module Consistency { + /** Holds if `s1` and `s2` are distinct representations of the same set. */ + query predicate nonUniqueSetRepresentation(Splits s1, Splits s2) { + forex(Split s | s = s1.getASplit() | s = s2.getASplit()) and + forex(Split s | s = s2.getASplit() | s = s1.getASplit()) and + s1 != s2 + } + + /** Holds if splitting invariant 2 is violated. */ + query predicate breakInvariant2( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, SplitImpl split, + Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = predSplits.getASplit() and + split.hasSuccessor(pred, succ, c) and + not split = succSplits.getASplit() + } + + /** Holds if splitting invariant 3 is violated. */ + query predicate breakInvariant3( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, SplitImpl split, + Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = predSplits.getASplit() and + split.hasExit(pred, succ, c) and + not split.hasEntry(pred, succ, c) and + split = succSplits.getASplit() + } + + /** Holds if splitting invariant 4 is violated. */ + query predicate breakInvariant4( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, SplitImpl split, + Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split.hasEntry(pred, succ, c) and + not split.getKind() = predSplits.getASplit().getKind() and + not split = succSplits.getASplit() and + split.getKind().isEnabled(succ) + } + + /** Holds if splitting invariant 5 is violated. */ + query predicate breakInvariant5( + AstNode pred, Splits predSplits, AstNode succ, Splits succSplits, SplitImpl split, + Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = succSplits.getASplit() and + not (split.hasSuccessor(pred, succ, c) and split = predSplits.getASplit()) and + not split.hasEntry(pred, succ, c) + } + + final private class SuccessorTypeFinal = SuccessorType; + + private class SimpleSuccessorType extends SuccessorTypeFinal { + SimpleSuccessorType() { + this = getAMatchingSuccessorType(any(Completion c | completionIsSimple(c))) + } + } + + private class NormalSuccessorType extends SuccessorTypeFinal { + NormalSuccessorType() { + this = getAMatchingSuccessorType(any(Completion c | completionIsNormal(c))) + } + } + + /** Holds if `node` has multiple successors of the same type `t`. */ + query predicate multipleSuccessors(Node node, SuccessorType t, Node successor) { + strictcount(getASuccessor(node, t)) > 1 and + successor = getASuccessor(node, t) and + // allow for functions with multiple bodies + not (t instanceof SimpleSuccessorType and node instanceof EntryNode) + } + + /** Holds if `node` has both a simple and a normal (non-simple) successor type. */ + query predicate simpleAndNormalSuccessors( + Node node, NormalSuccessorType t1, SimpleSuccessorType t2, Node succ1, Node succ2 + ) { + t1 != t2 and + succ1 = getASuccessor(node, t1) and + succ2 = getASuccessor(node, t2) + } + + /** Holds if `node` is lacking a successor. */ + query predicate deadEnd(Node node) { + not node instanceof ExitNode and + not exists(getASuccessor(node, _)) + } + + /** Holds if `split` has multiple kinds. */ + query predicate nonUniqueSplitKind(SplitImpl split, SplitKind sk) { + sk = split.getKind() and + strictcount(split.getKind()) > 1 + } + + /** Holds if `sk` has multiple integer representations. */ + query predicate nonUniqueListOrder(SplitKind sk, int ord) { + ord = sk.getListOrder() and + strictcount(sk.getListOrder()) > 1 + } + } +} diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml new file mode 100644 index 00000000000..ce3dd39e158 --- /dev/null +++ b/shared/controlflow/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/controlflow +version: 0.0.1 +groups: shared +library: true +dependencies: + codeql/util: ${workspace} +warnOnImplicitThis: true diff --git a/shared/dataflow/CHANGELOG.md b/shared/dataflow/CHANGELOG.md new file mode 100644 index 00000000000..8510e78ec2c --- /dev/null +++ b/shared/dataflow/CHANGELOG.md @@ -0,0 +1,9 @@ +## 0.0.1 + +### New Features + +* The `StateConfigSig` signature now supports a unary `isSink` predicate that does not specify the `FlowState` for which the given node is a sink. Instead, any `FlowState` is considered a valid `FlowState` for such a sink. + +### Minor Analysis Improvements + +* Initial release. Moves the shared inter-procedural data-flow library into its own qlpack. diff --git a/shared/dataflow/change-notes/released/0.0.1.md b/shared/dataflow/change-notes/released/0.0.1.md new file mode 100644 index 00000000000..8510e78ec2c --- /dev/null +++ b/shared/dataflow/change-notes/released/0.0.1.md @@ -0,0 +1,9 @@ +## 0.0.1 + +### New Features + +* The `StateConfigSig` signature now supports a unary `isSink` predicate that does not specify the `FlowState` for which the given node is a sink. Instead, any `FlowState` is considered a valid `FlowState` for such a sink. + +### Minor Analysis Improvements + +* Initial release. Moves the shared inter-procedural data-flow library into its own qlpack. diff --git a/shared/dataflow/codeql-pack.release.yml b/shared/dataflow/codeql-pack.release.yml new file mode 100644 index 00000000000..c6933410b71 --- /dev/null +++ b/shared/dataflow/codeql-pack.release.yml @@ -0,0 +1,2 @@ +--- +lastReleaseVersion: 0.0.1 diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll new file mode 100644 index 00000000000..ce2c518f478 --- /dev/null +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -0,0 +1,684 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * adds a global analysis, mainly exposed through the `Global` and `GlobalWithState` + * modules. + */ + +/** Provides language-specific data flow parameters. */ +signature module InputSig { + class Node { + /** Gets a textual representation of this element. */ + string toString(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); + } + + class ParameterNode extends Node; + + class ArgumentNode extends Node; + + class ReturnNode extends Node { + ReturnKind getKind(); + } + + class OutNode extends Node; + + class PostUpdateNode extends Node { + Node getPreUpdateNode(); + } + + class CastNode extends Node; + + predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos); + + predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos); + + DataFlowCallable nodeGetEnclosingCallable(Node node); + + DataFlowType getNodeType(Node node); + + predicate nodeIsHidden(Node node); + + class DataFlowExpr; + + /** Gets the node corresponding to `e`. */ + Node exprNode(DataFlowExpr e); + + class DataFlowCall { + /** Gets a textual representation of this element. */ + string toString(); + + DataFlowCallable getEnclosingCallable(); + } + + class DataFlowCallable { + /** Gets a textual representation of this element. */ + string toString(); + } + + class ReturnKind { + /** Gets a textual representation of this element. */ + string toString(); + } + + /** Gets a viable implementation of the target of the given `Call`. */ + DataFlowCallable viableCallable(DataFlowCall c); + + /** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. + */ + predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c); + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ + DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx); + + /** + * Gets a node that can read the value returned from `call` with return kind + * `kind`. + */ + OutNode getAnOutNode(DataFlowCall call, ReturnKind kind); + + class DataFlowType { + /** Gets a textual representation of this element. */ + string toString(); + } + + string ppReprType(DataFlowType t); + + bindingset[t1, t2] + predicate compatibleTypes(DataFlowType t1, DataFlowType t2); + + predicate typeStrongerThan(DataFlowType t1, DataFlowType t2); + + class Content { + /** Gets a textual representation of this element. */ + string toString(); + } + + predicate forceHighPrecision(Content c); + + /** + * An entity that represents a set of `Content`s. + * + * The set may be interpreted differently depending on whether it is + * stored into (`getAStoreContent`) or read from (`getAReadContent`). + */ + class ContentSet { + /** Gets a content that may be stored into when storing into this set. */ + Content getAStoreContent(); + + /** Gets a content that may be read from when reading from this set. */ + Content getAReadContent(); + } + + class ContentApprox { + /** Gets a textual representation of this element. */ + string toString(); + } + + ContentApprox getContentApprox(Content c); + + class ParameterPosition { + /** Gets a textual representation of this element. */ + bindingset[this] + string toString(); + } + + class ArgumentPosition { + /** Gets a textual representation of this element. */ + bindingset[this] + string toString(); + } + + predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos); + + predicate simpleLocalFlowStep(Node node1, Node node2); + + /** + * Holds if data can flow from `node1` to `node2` through a non-local step + * that does not follow a call edge. For example, a step through a global + * variable. + */ + predicate jumpStep(Node node1, Node node2); + + /** + * Holds if data can flow from `node1` to `node2` via a read of `c`. Thus, + * `node1` references an object with a content `c.getAReadContent()` whose + * value ends up in `node2`. + */ + predicate readStep(Node node1, ContentSet c, Node node2); + + /** + * Holds if data can flow from `node1` to `node2` via a store into `c`. Thus, + * `node2` references an object with a content `c.getAStoreContent()` that + * contains the value of `node1`. + */ + predicate storeStep(Node node1, ContentSet c, Node node2); + + /** + * Holds if values stored inside content `c` are cleared at node `n`. For example, + * any value stored inside `f` is cleared at the pre-update node associated with `x` + * in `x.f = newValue`. + */ + predicate clearsContent(Node n, ContentSet c); + + /** + * Holds if the value that is being tracked is expected to be stored inside content `c` + * at node `n`. + */ + predicate expectsContent(Node n, ContentSet c); + + /** + * Holds if the node `n` is unreachable when the call context is `call`. + */ + predicate isUnreachableInCall(Node n, DataFlowCall call); + + default int accessPathLimit() { result = 5 } + + /** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ + predicate allowParameterReturnInSelf(ParameterNode p); + + class LambdaCallKind; + + /** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ + predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c); + + /** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ + predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver); + + /** Extra data-flow steps needed for lambda flow analysis. */ + predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue); + + /** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ + default predicate neverSkipInPathGraph(Node n) { none() } + + /** + * Gets an additional term that is added to the `join` and `branch` computations to reflect + * an additional forward or backwards branching factor that is not taken into account + * when calculating the (virtual) dispatch cost. + * + * Argument `arg` is part of a path from a source to a sink, and `p` is the target parameter. + */ + int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p); + + predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg); +} + +module Configs { + private import Lang + private import internal.DataFlowImplCommon::MakeImplCommon + import DataFlowImplCommonPublic + + /** An input configuration for data flow. */ + signature module ConfigSig { + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Node sink); + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + default predicate isBarrier(Node node) { none() } + + /** Holds if data flow into `node` is prohibited. */ + default predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + default predicate isBarrierOut(Node node) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + default predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Holds if `node` should never be skipped over in the `PathGraph` and in path + * explanations. + */ + default predicate neverSkip(Node node) { + isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) + } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + default int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + * + * These features are generally not relevant for typical end-to-end data flow + * queries, but should only be used for constructing paths that need to + * somehow be pluggable in another path context. + */ + default FlowFeature getAFeature() { none() } + + /** Holds if sources should be grouped in the result of `flowPath`. */ + default predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `flowPath`. */ + default predicate sinkGrouping(Node sink, string sinkGroup) { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (as it is in a `path-problem` query). + */ + default predicate includeHiddenNodes() { none() } + } + + /** An input configuration for data flow using flow state. */ + signature module StateConfigSig { + bindingset[this] + class FlowState; + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state); + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node sink, FlowState state); + + /** + * Holds if `sink` is a relevant data flow sink for any state. + */ + default predicate isSink(Node sink) { none() } + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + default predicate isBarrier(Node node) { none() } + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + default predicate isBarrier(Node node, FlowState state) { none() } + + /** Holds if data flow into `node` is prohibited. */ + default predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + default predicate isBarrierOut(Node node) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + default predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2 + ) { + none() + } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + default predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Holds if `node` should never be skipped over in the `PathGraph` and in path + * explanations. + */ + default predicate neverSkip(Node node) { + isAdditionalFlowStep(node, _) or + isAdditionalFlowStep(_, node) or + isAdditionalFlowStep(node, _, _, _) or + isAdditionalFlowStep(_, _, node, _) + } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + default int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + * + * These features are generally not relevant for typical end-to-end data flow + * queries, but should only be used for constructing paths that need to + * somehow be pluggable in another path context. + */ + default FlowFeature getAFeature() { none() } + + /** Holds if sources should be grouped in the result of `flowPath`. */ + default predicate sourceGrouping(Node source, string sourceGroup) { none() } + + /** Holds if sinks should be grouped in the result of `flowPath`. */ + default predicate sinkGrouping(Node sink, string sinkGroup) { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (as it is in a `path-problem` query). + */ + default predicate includeHiddenNodes() { none() } + } +} + +module DataFlowMake { + private import Lang + private import internal.DataFlowImpl::MakeImpl + import Configs + + /** + * Gets the exploration limit for `partialFlow` and `partialFlowRev` + * measured in approximate number of interprocedural steps. + */ + signature int explorationLimitSig(); + + /** + * The output of a global data flow computation. + */ + signature module GlobalFlowSig { + /** + * A `Node` augmented with a call context (except for sinks) and an access path. + * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. + */ + class PathNode; + + /** + * Holds if data can flow from `source` to `sink`. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate flowPath(PathNode source, PathNode sink); + + /** + * Holds if data can flow from `source` to `sink`. + */ + predicate flow(Node source, Node sink); + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowTo(Node sink); + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowToExpr(DataFlowExpr sink); + } + + /** + * Constructs a global data flow computation. + */ + module Global implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import DefaultState + import Config + } + + import Impl + } + + /** DEPRECATED: Use `Global` instead. */ + deprecated module Make implements GlobalFlowSig { + import Global + } + + /** + * Constructs a global data flow computation using flow state. + */ + module GlobalWithState implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import Config + } + + import Impl + } + + /** DEPRECATED: Use `GlobalWithState` instead. */ + deprecated module MakeWithState implements GlobalFlowSig { + import GlobalWithState + } + + signature class PathNodeSig { + /** Gets a textual representation of this element. */ + string toString(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); + + /** Gets the underlying `Node`. */ + Node getNode(); + } + + signature module PathGraphSig { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + predicate edges(PathNode a, PathNode b); + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + predicate nodes(PathNode n, string key, string val); + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); + } + + /** + * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. + */ + module MergePathGraph< + PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, + PathGraphSig Graph2> + { + private newtype TPathNode = + TPathNode1(PathNode1 p) or + TPathNode2(PathNode2 p) + + /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ + class PathNode extends TPathNode { + /** Gets this as a projection on the first given `PathGraph`. */ + PathNode1 asPathNode1() { this = TPathNode1(result) } + + /** Gets this as a projection on the second given `PathGraph`. */ + PathNode2 asPathNode2() { this = TPathNode2(result) } + + /** Gets a textual representation of this element. */ + string toString() { + result = this.asPathNode1().toString() or + result = this.asPathNode2().toString() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or + this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + Node getNode() { + result = this.asPathNode1().getNode() or + result = this.asPathNode2().getNode() + } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph implements PathGraphSig { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { + Graph1::edges(a.asPathNode1(), b.asPathNode1()) or + Graph2::edges(a.asPathNode2(), b.asPathNode2()) + } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + Graph1::nodes(n.asPathNode1(), key, val) or + Graph2::nodes(n.asPathNode2(), key, val) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or + Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) + } + } + } + + /** + * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. + */ + module MergePathGraph3< + PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, + PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> + { + private module MergedInner = MergePathGraph; + + private module Merged = + MergePathGraph; + + /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ + class PathNode instanceof Merged::PathNode { + /** Gets this as a projection on the first given `PathGraph`. */ + PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } + + /** Gets this as a projection on the second given `PathGraph`. */ + PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } + + /** Gets this as a projection on the third given `PathGraph`. */ + PathNode3 asPathNode3() { result = super.asPathNode2() } + + /** Gets a textual representation of this element. */ + string toString() { result = super.toString() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + Node getNode() { result = super.getNode() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph implements PathGraphSig { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { Merged::PathGraph::edges(a, b) } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + Merged::PathGraph::nodes(n, key, val) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Merged::PathGraph::subpaths(arg, par, ret, out) + } + } + } +} diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll new file mode 100644 index 00000000000..d6c05ca042a --- /dev/null +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -0,0 +1,4794 @@ +/** + * INTERNAL: Do not use. + * + * Provides an implementation of global (interprocedural) data flow. + */ + +private import codeql.util.Unit +private import codeql.util.Option +private import codeql.dataflow.DataFlow + +module MakeImpl { + private import Lang + private import DataFlowMake + private import DataFlowImplCommon::MakeImplCommon + private import DataFlowImplCommonPublic + + /** + * An input configuration for data flow using flow state. This signature equals + * `StateConfigSig`, but requires explicit implementation of all predicates. + */ + signature module FullStateConfigSig { + bindingset[this] + class FlowState; + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state); + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node sink, FlowState state); + + /** + * Holds if `sink` is a relevant data flow sink for any state. + */ + predicate isSink(Node sink); + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node); + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + predicate isBarrier(Node node, FlowState state); + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node); + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node); + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + predicate isAdditionalFlowStep(Node node1, Node node2); + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, ContentSet c); + + /** + * Holds if `node` should never be skipped over in the `PathGraph` and in path + * explanations. + */ + predicate neverSkip(Node node); + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit(); + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + * + * These features are generally not relevant for typical end-to-end data flow + * queries, but should only be used for constructing paths that need to + * somehow be pluggable in another path context. + */ + FlowFeature getAFeature(); + + /** Holds if sources should be grouped in the result of `flowPath`. */ + predicate sourceGrouping(Node source, string sourceGroup); + + /** Holds if sinks should be grouped in the result of `flowPath`. */ + predicate sinkGrouping(Node sink, string sinkGroup); + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (as it is in a `path-problem` query). + */ + predicate includeHiddenNodes(); + } + + /** + * Provides default `FlowState` implementations given a `StateConfigSig`. + */ + module DefaultState { + class FlowState = Unit; + + predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } + + predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } + + predicate isBarrier(Node node, FlowState state) { none() } + + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } + } + + /** + * Constructs a data flow computation given a full input configuration. + */ + module Impl { + private class FlowState = Config::FlowState; + + private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + Config::allowImplicitRead(n, _) and hasRead = [false, true] + } + + private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } + } + + private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.asNode().(ParamNode).isParameterOf(c, pos) + } + + ParameterPosition getPosition() { this.isParameterOf(_, result) } + } + + private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } + } + + private predicate inBarrier(NodeEx node) { + exists(Node n | + node.asNode() = n and + Config::isBarrierIn(n) and + Config::isSource(n, _) + ) + } + + private predicate outBarrier(NodeEx node) { + exists(Node n | + node.asNode() = n and + Config::isBarrierOut(n) + | + Config::isSink(n, _) + or + Config::isSink(n) + ) + } + + pragma[nomagic] + private predicate fullBarrier(NodeEx node) { + exists(Node n | node.asNode() = n | + Config::isBarrier(n) + or + Config::isBarrierIn(n) and + not Config::isSource(n, _) + or + Config::isBarrierOut(n) and + not Config::isSink(n, _) and + not Config::isSink(n) + ) + } + + pragma[nomagic] + private predicate stateBarrier(NodeEx node, FlowState state) { + exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) + } + + pragma[nomagic] + private predicate sourceNode(NodeEx node, FlowState state) { + Config::isSource(node.asNode(), state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + pragma[nomagic] + private predicate sinkNodeWithState(NodeEx node, FlowState state) { + Config::isSink(node.asNode(), state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + /** Provides the relevant barriers for a step from `node1` to `node2`. */ + pragma[inline] + private predicate stepFilter(NodeEx node1, NodeEx node2) { + not outBarrier(node1) and + not inBarrier(node2) and + not fullBarrier(node1) and + not fullBarrier(node2) + } + + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + + /** + * Holds if data can flow in one local step from `node1` to `node2`. + */ + private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2) + ) + or + exists(Node n | + Config::allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) and + not fullBarrier(node1) + ) + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) + ) + or + exists(Node n | + Config::allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n and + not fullBarrier(node2) + ) + } + + private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2 + ) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) and + not stateBarrier(node1, s1) and + not stateBarrier(node2, s2) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ + private predicate jumpStepEx(NodeEx node1, NodeEx node2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) and + not stateBarrier(node1, s1) and + not stateBarrier(node2, s2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + pragma[nomagic] + private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { + readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and + stepFilter(node1, node2) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + Config::allowImplicitRead(n, c) + ) + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + private predicate read(NodeEx node1, Content c, NodeEx node2) { + exists(ContentSet cs | + readSetEx(node1, cs, node2) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + pragma[nomagic] + private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + + pragma[nomagic] + private predicate hasReadStep(Content c) { read(_, c, _) } + + pragma[nomagic] + private predicate storeEx( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ) { + store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), + contentType, containerType) and + hasReadStep(c) and + stepFilter(node1, node2) + } + + pragma[nomagic] + private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) + } + + pragma[nomagic] + private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } + + private predicate hasSourceCallCtx() { + exists(FlowFeature feature | feature = Config::getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) + } + + private predicate sourceCallCtx(CallContext cc) { + if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny + } + + private predicate hasSinkCallCtx() { + exists(FlowFeature feature | feature = Config::getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) + } + + /** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ + bindingset[p, kind] + private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfCached(p.asNode()) + ) + } + + private module Stage1 implements StageSig { + class Ap = Unit; + + private class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + private predicate fwdFlow(NodeEx node, Cc cc) { + sourceNode(node, _) and + if hasSourceCallCtx() then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc) | + localFlowStepEx(mid, node) or + additionalLocalFlowStep(mid, node) or + additionalLocalStateStep(mid, _, node, _) + ) + or + exists(NodeEx mid | fwdFlow(mid, _) and cc = false | + jumpStepEx(mid, node) or + additionalJumpStep(mid, node) or + additionalJumpStateStep(mid, _, node, _) + ) + or + // store + exists(NodeEx mid | + useFieldFlow() and + fwdFlow(mid, cc) and + storeEx(mid, _, node, _, _) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc) and + fwdFlowConsCandSet(c, _) + ) + or + // flow into a callable + fwdFlowIn(_, _, _, node) and + cc = true + or + // flow out of a callable + fwdFlowOut(_, node, false) and + cc = false + or + // flow through a callable + exists(DataFlowCall call | + fwdFlowOutFromArg(call, node) and + fwdFlowIsEntered(call, cc) + ) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { + // call context cannot help reduce virtual dispatch + fwdFlow(arg, cc) and + viableParamArgEx(call, p, arg) and + not fullBarrier(p) and + ( + cc = false + or + cc = true and + not reducedViableImplInCallContext(call, _, _) + ) + or + // call context may help reduce virtual dispatch + exists(DataFlowCallable target | + fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and + target = viableImplInSomeFwdFlowCallContextExt(call) and + cc = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } + + pragma[nomagic] + private predicate fwdFlowInReducedViableImplInSomeCallContext( + DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target + ) { + fwdFlow(arg, true) and + viableParamArgEx(call, p, arg) and + reducedViableImplInCallContext(call, _, _) and + target = p.getEnclosingCallable() and + not fullBarrier(p) + } + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference, + * and to `ctx`s that are reachable in `fwdFlow`. + */ + pragma[nomagic] + private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { + exists(DataFlowCall ctx | + fwdFlowIsEntered(ctx, _) and + result = viableImplInCallContextExt(call, ctx) + ) + } + + private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { + exists(NodeEx mid | + fwdFlow(mid, cc) and + readSetEx(mid, c, node) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c) { + exists(NodeEx mid, NodeEx node | + not fullBarrier(node) and + useFieldFlow() and + fwdFlow(mid, _) and + storeEx(mid, c, node, _, _) + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { + fwdFlowConsCand(c) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { + exists(RetNodeEx ret | + fwdFlow(ret, cc) and + ret.getReturnPosition() = pos + ) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { + fwdFlowOut(call, out, true) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2) or + additionalJumpStateStep(node1, state1, _, state2) + | + fwdFlow(node1) + ) + } + + private predicate fwdFlowState(FlowState state) { + sourceNode(_, state) + or + exists(FlowState state0 | + fwdFlowState(state0) and + stateStepFwd(state0, state) + ) + } + + additional predicate sinkNode(NodeEx node, FlowState state) { + fwdFlow(node) and + fwdFlowState(state) and + Config::isSink(node.asNode()) + or + fwdFlow(node) and + fwdFlowState(state) and + sinkNodeWithState(node, state) + } + + /** + * Holds if `node` is part of a path from a source to a sink. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + private predicate revFlow(NodeEx node, boolean toReturn) { + revFlow0(node, toReturn) and + fwdFlow(node) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn) { + sinkNode(node, _) and + if hasSinkCallCtx() then toReturn = true else toReturn = false + or + exists(NodeEx mid | revFlow(mid, toReturn) | + localFlowStepEx(node, mid) or + additionalLocalFlowStep(node, mid) or + additionalLocalStateStep(node, _, mid, _) + ) + or + exists(NodeEx mid | revFlow(mid, _) and toReturn = false | + jumpStepEx(node, mid) or + additionalJumpStep(node, mid) or + additionalJumpStateStep(node, _, mid, _) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn) and + revFlowConsCand(c) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSetEx(node, c, mid) and + fwdFlowConsCandSet(c, _) and + revFlow(mid, toReturn) + ) + or + // flow into a callable + revFlowIn(_, node, false) and + toReturn = false + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + or + // flow through a callable + exists(DataFlowCall call | + revFlowInToReturn(call, node) and + revFlowIsReturned(call, toReturn) + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node) and + readSetEx(node, cs, mid) and + fwdFlowConsCandSet(cs, c) and + revFlow(pragma[only_bind_into](mid), _) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { + exists(NodeEx mid | + revFlow(mid, toReturn) and + fwdFlowConsCand(c) and + storeEx(node, c, mid, _, _) + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + additional predicate revFlowIsReadAndStored(Content c) { + revFlowConsCand(c) and + revFlowStore(c, _, _) + } + + pragma[nomagic] + additional predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out + ) { + fwdFlowReturnPosition(pos, _) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos) { + exists(NodeEx out | + revFlow(out, _) and + viableReturnPosOutNodeCandFwd1(_, pos, out) + ) + } + + pragma[nomagic] + additional predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg + ) { + fwdFlowIn(call, arg, _, p) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { + exists(ParamNodeEx p | + revFlow(p, toReturn) and + viableParamArgNodeCandFwd1(call, p, arg) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { + revFlowIn(call, arg, true) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { + exists(NodeEx out | + revFlow(out, toReturn) and + fwdFlowOutFromArg(call, out) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2) or + additionalJumpStateStep(node1, state1, node2, state2) + | + revFlow(node1, _) and + revFlow(node2, _) and + fwdFlowState(state1) and + fwdFlowState(state2) + ) + } + + pragma[nomagic] + additional predicate revFlowState(FlowState state) { + exists(NodeEx node | + sinkNode(node, state) and + revFlow(node, _) and + fwdFlowState(state) + ) + or + exists(FlowState state0 | + revFlowState(state0) and + stateStepRev(state, state0) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, + DataFlowType containerType + ) { + revFlowIsReadAndStored(c) and + revFlow(node2) and + storeEx(node1, c, node2, contentType, containerType) and + exists(ap1) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { + revFlowIsReadAndStored(c) and + read(n1, c, n2) and + revFlow(n2) + } + + pragma[nomagic] + predicate revFlow(NodeEx node) { revFlow(node, _) } + + pragma[nomagic] + predicate revFlowAp(NodeEx node, Ap ap) { + revFlow(node) and + exists(ap) + } + + bindingset[node, state] + predicate revFlow(NodeEx node, FlowState state, Ap ap) { + revFlow(node, _) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node) { + revFlow(node, true) and + fwdFlow(node, true) and + not inBarrier(node) and + not outBarrier(node) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { + exists(DataFlowCallable c, ReturnKindExt kind | + throughFlowNodeCand(p) and + returnFlowCallableNodeCand(c, kind) and + p.getEnclosingCallable() = c and + exists(ap) and + parameterFlowThroughAllowed(p, kind) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { + throughFlowNodeCand(ret) and + kind = ret.getKind() and + exists(argAp) and + exists(ap) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn) and + revFlowInToReturn(call, arg) and + revFlowIsReturned(call, toReturn) + ) + } + + additional predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node)) and + fields = count(Content f0 | fwdFlowConsCand(f0)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _)) and + fields = count(Content f0 | revFlowConsCand(f0)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b)) + } + /* End: Stage 1 logic. */ + } + + private predicate sinkNode = Stage1::sinkNode/2; + + pragma[noinline] + private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { + Stage1::revFlow(node2) and + localFlowStepEx(node1, node2) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { + Stage1::revFlow(node2) and + additionalLocalFlowStep(node1, node2) + } + + pragma[nomagic] + private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { + Stage1::revFlow(out) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) + } + + /** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + pragma[nomagic] + private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out + ) { + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret) and + not outBarrier(ret) and + not inBarrier(out) + ) + } + + pragma[nomagic] + private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg) and + Stage1::revFlow(arg) + } + + /** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ + pragma[nomagic] + private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { + viableParamArgNodeCand1(call, p, arg) and + Stage1::revFlow(p) and + not outBarrier(arg) and + not inBarrier(p) + } + + /** + * Gets an additional term that is added to `branch` and `join` when deciding whether + * the amount of forward or backward branching is within the limit specified by the + * configuration. + */ + pragma[nomagic] + private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { + flowIntoCallNodeCand1(_, arg, p) and + result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + pragma[nomagic] + private int branch(NodeEx n1) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n) + ) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + pragma[nomagic] + private int join(NodeEx n2) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2) + ) + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) + } + + /** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + pragma[nomagic] + private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow + ) { + flowOutOfCallNodeCand1(call, ret, kind, out) and + exists(int b, int j | + b = branch(ret) and + j = join(out) and + if b.minimum(j) <= Config::fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + pragma[nomagic] + private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow + ) { + flowIntoCallNodeCand1(call, arg, p) and + exists(int b, int j | + b = branch(arg) and + j = join(p) and + if b.minimum(j) <= Config::fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) + } + + private signature module StageSig { + class Ap; + + predicate revFlow(NodeEx node); + + predicate revFlowAp(NodeEx node, Ap ap); + + bindingset[node, state] + predicate revFlow(NodeEx node, FlowState state, Ap ap); + + predicate callMayFlowThroughRev(DataFlowCall call); + + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); + + predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); + + predicate storeStepCand( + NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, + DataFlowType containerType + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2); + } + + private module MkStage { + class ApApprox = PrevStage::Ap; + + signature module StageParam { + class Typ { + string toString(); + } + + class Ap; + + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + Typ getTyp(DataFlowType t); + + bindingset[c, t, tail] + Ap apCons(Content c, Typ t, Ap tail); + + /** + * An approximation of `Content` that corresponds to the precision level of + * `Ap`, such that the mappings from both `Ap` and `Content` to this type + * are functional. + */ + class ApHeadContent; + + ApHeadContent getHeadContent(Ap ap); + + ApHeadContent projectToHeadContent(Content c); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1] + bindingset[node2, state2] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow + ); + + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); + + bindingset[typ, contentType] + predicate typecheckStore(Typ typ, DataFlowType contentType); + } + + module Stage implements StageSig { + import Param + + /* Begin: Stage logic. */ + private module TypOption = Option; + + private class TypOption = TypOption::Option; + + pragma[nomagic] + private Typ getNodeTyp(NodeEx node) { + PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) + } + + pragma[nomagic] + private predicate flowIntoCallApa( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa + ) { + flowIntoCall(call, arg, p, allowsFieldFlow) and + PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and + PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) + } + + pragma[nomagic] + private predicate flowOutOfCallApa( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, + ApApprox apa + ) { + flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and + PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and + PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + ApApprox argApa, ApApprox apa + ) { + exists(ReturnKindExt kind | + flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and + PrevStage::callMayFlowThroughRev(call) and + PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and + matchesCall(ccc, call) + ) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `summaryCtx` and `argAp` record the + * corresponding parameter position and access path of that argument, respectively. + */ + pragma[nomagic] + additional predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, + ApOption argAp, Typ t, Ap ap, ApApprox apa + ) { + fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) + } + + private predicate fwdFlow1( + NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, + ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa + ) { + fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and + PrevStage::revFlow(node, state, apa) and + filter(node, state, t0, ap, t) + } + + pragma[nomagic] + private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { + fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t + } + + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, + ApOption argAp, Typ t, Ap ap, ApApprox apa + ) { + sourceNode(node, state) and + (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and + argT instanceof TypOption::None and + argAp = apNone() and + summaryCtx = TParamNodeNone() and + t = getNodeTyp(node) and + ap instanceof ApNil and + apa = getApprox(ap) + or + exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | + fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, localCc) and + t = t0 + or + localStep(mid, state0, node, state, false, t, localCc) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, state, _, _, _, _, t, ap, apa) and + jumpStepEx(mid, node) and + cc = ccNone() and + summaryCtx = TParamNodeNone() and + argT instanceof TypOption::None and + argAp = apNone() + ) + or + exists(NodeEx mid | + fwdFlow(mid, state, _, _, _, _, _, ap, apa) and + additionalJumpStep(mid, node) and + cc = ccNone() and + summaryCtx = TParamNodeNone() and + argT instanceof TypOption::None and + argAp = apNone() and + t = getNodeTyp(node) and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0 | + fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and + additionalJumpStateStep(mid, state0, node, state) and + cc = ccNone() and + summaryCtx = TParamNodeNone() and + argT instanceof TypOption::None and + argAp = apNone() and + t = getNodeTyp(node) and + ap instanceof ApNil + ) + or + // store + exists(Content c, Typ t0, Ap ap0 | + fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and + ap = apCons(c, t0, ap0) and + apa = getApprox(ap) + ) + or + // read + exists(Typ t0, Ap ap0, Content c | + fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and + fwdFlowConsCand(t0, ap0, c, t, ap) and + apa = getApprox(ap) + ) + or + // flow into a callable + fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and + if PrevStage::parameterMayFlowThrough(node, apa) + then ( + summaryCtx = TParamNodeSome(node.asNode()) and + argT = TypOption::some(t) and + argAp = apSome(ap) + ) else ( + summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() + ) + or + // flow out of a callable + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and + flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and + inner = ret.getEnclosingCallable() and + cc = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + or + // flow through a callable + exists( + DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, + ApApprox innerArgApa + | + fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, + innerArgApa) and + flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, + ParamNodeOption summaryCtx, TypOption argT, ApOption argAp + ) { + exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | + fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and + PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and + t2 = getTyp(containerType) and + typecheckStore(t1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` and type `t1` reaches a + * store of `c` on a container of type `t2` resulting in access path + * `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { + fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and + cons = apCons(c, t1, tail) + or + exists(Typ t0 | + typeStrengthen(t0, cons, t2) and + fwdFlowConsCand(t0, cons, c, t1, tail) + ) + } + + pragma[nomagic] + private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + PrevStage::readStepCand(node1, c, node2) and + apc = projectToHeadContent(c) + } + + bindingset[node1, apc] + pragma[inline_late] + private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + readStepCand(node1, apc, c, node2) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, + ParamNodeOption summaryCtx, TypOption argT, ApOption argAp + ) { + exists(ApHeadContent apc | + fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and + apc = getHeadContent(ap) and + readStepCand0(node1, apc, c, node2) + ) + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, + ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and + flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowRetFromArg( + RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, + ApApprox argApa, Typ t, Ap ap, ApApprox apa + ) { + exists(ReturnKindExt kind | + fwdFlow(pragma[only_bind_into](ret), state, ccc, + TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), + pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and + kind = ret.getKind() and + parameterFlowThroughAllowed(summaryCtx, kind) and + argApa = getApprox(argAp) and + PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) + ) + } + + pragma[inline] + private predicate fwdFlowThrough0( + DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, + TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, + ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa + ) { + fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, + ap, apa) and + fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, + innerArgAp) + } + + pragma[nomagic] + private predicate fwdFlowThrough( + DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, + TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, + ApApprox innerArgApa + ) { + fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, + innerArgApa) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, + ApOption argAp, ParamNodeEx p, Typ t, Ap ap + ) { + exists(ApApprox apa | + fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, + ap, pragma[only_bind_into](apa)) and + PrevStage::parameterMayFlowThrough(p, apa) and + PrevStage::callMayFlowThroughRev(call) + ) + } + + pragma[nomagic] + private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { + fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and + ap2 = apCons(c, t1, ap1) and + readStepFwd(_, ap2, c, _, _) + } + + pragma[nomagic] + private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { + exists(Typ t1 | + fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and + fwdFlowConsCand(t1, ap1, c, _, ap2) + ) + } + + pragma[nomagic] + private predicate returnFlowsThrough0( + DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, + ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa + ) { + fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, + innerArgAp, innerArgApa) + } + + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, + Ap argAp, Ap ap + ) { + exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | + returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and + flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and + pos = ret.getReturnPosition() and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap + ) { + exists(ApApprox argApa, Typ argT | + flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), + allowsFieldFlow, argApa) and + fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), + argApa) and + returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), + pragma[only_bind_into](argAp), ap) and + if allowsFieldFlow = false then argAp instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate flowIntoCallAp( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap + ) { + exists(ApApprox apa | + flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and + fwdFlow(arg, _, _, _, _, _, _, ap, apa) + ) + } + + pragma[nomagic] + private predicate flowOutOfCallAp( + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, + Ap ap + ) { + exists(ApApprox apa | + flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and + fwdFlow(ret, _, _, _, _, _, _, ap, apa) and + pos = ret.getReturnPosition() + ) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink. + * + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. + */ + pragma[nomagic] + additional predicate revFlow( + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + ) { + revFlow0(node, state, returnCtx, returnAp, ap) and + fwdFlow(node, state, _, _, _, _, _, ap, _) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + ) { + fwdFlow(node, state, _, _, _, _, _, ap, _) and + sinkNode(node, state) and + ( + if hasSinkCallCtx() + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, _) and + revFlow(mid, state0, returnCtx, returnAp, ap) + ) + or + exists(NodeEx mid, FlowState state0 | + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and + revFlow(mid, state0, returnCtx, returnAp, ap) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStepEx(node, mid) and + revFlow(mid, state, _, _, ap) and + returnCtx = TReturnCtxNone() and + returnAp = apNone() + ) + or + exists(NodeEx mid | + additionalJumpStep(node, mid) and + revFlow(pragma[only_bind_into](mid), state, _, _, ap) and + returnCtx = TReturnCtxNone() and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0 | + additionalJumpStateStep(node, state, mid, state0) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and + returnCtx = TReturnCtxNone() and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and + revFlowConsCand(ap0, c, ap) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, returnCtx, returnAp, ap0) and + readStepFwd(node, ap, _, mid, ap0) + ) + or + // flow into a callable + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, TReturnCtxNone(), returnAp, ap) and + flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + returnCtx = TReturnCtxNone() + ) + or + // flow through a callable + exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | + revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and + flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, ap) and + if returnFlowsThrough(node, pos, state, _, _, _, _, ap) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) + ) + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, + ReturnCtx returnCtx, ApOption returnAp + ) { + revFlow(mid, state, returnCtx, returnAp, ap0) and + storeStepFwd(node, t, ap, c, mid, ap0) + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, + ReturnCtx returnCtx, ApOption returnAp, Ap ap + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, returnCtx, returnAp, ap) and + flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowParamToReturn( + ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap + ) { + revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), + apSome(returnAp), pragma[only_bind_into](ap)) and + parameterFlowThroughAllowed(p, pos.getKind()) and + PrevStage::parameterMayFlowThrough(p, getApprox(ap)) + } + + pragma[nomagic] + private predicate revFlowThrough( + DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, + ReturnPosition pos, ApOption returnAp, Ap ap, Ap innerReturnAp + ) { + revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and + revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and + returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, + DataFlowType containerType + ) { + exists(Ap ap2 | + PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and + revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and + revFlowConsCand(ap2, c, ap1) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and + readStepFwd(node1, ap1, c, node2, ap2) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) + ) + } + + additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } + + predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } + + pragma[nomagic] + predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } + + pragma[nomagic] + predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } + + private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } + + private predicate revConsCand(Content c, Typ t, Ap ap) { + exists(Ap ap2 | + revFlowStore(ap2, c, ap, t, _, _, _, _, _) and + revFlowConsCand(ap2, c, ap) + ) + } + + private predicate validAp(Ap ap) { + revFlow(_, _, _, _, ap) and ap instanceof ApNil + or + exists(Content head, Typ t, Ap tail | + consCand(head, t, tail) and + ap = apCons(head, t, tail) + ) + } + + additional predicate consCand(Content c, Typ t, Ap ap) { + revConsCand(c, t, ap) and + validAp(ap) + } + + pragma[nomagic] + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp + ) { + revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and + parameterFlowThroughAllowed(p, pos.getKind()) + } + + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { + exists(ReturnPosition pos | + returnFlowsThrough(_, pos, _, _, p, _, ap, _) and + parameterFlowsThroughRev(p, ap, pos, _) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { + exists(ParamNodeEx p, ReturnPosition pos | + returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and + parameterFlowsThroughRev(p, argAp, pos, ap) and + kind = pos.getKind() + ) + } + + pragma[nomagic] + private predicate revFlowThroughArg( + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, + Ap ap + ) { + exists(ParamNodeEx p, Ap innerReturnAp | + revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and + flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call) { + exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | + revFlow(arg, state, returnCtx, returnAp, ap) and + revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) + ) + } + + additional predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and + fields = count(Content f0 | fwdConsCand(f0, _, _)) and + conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, + ApOption argAp, Typ t, Ap ap | + fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and + fields = count(Content f0 | consCand(f0, _, _)) and + conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and + states = count(FlowState state | revFlow(_, state, _, _, _)) and + tuples = + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap) + ) + } + /* End: Stage logic. */ + } + } + + private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } + } + + private module Level1CallContext { + class Cc = CallContext; + + class CcCall = CallContextCall; + + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + CcCall ccSomeCall() { result instanceof CallContextSomeCall } + + module NoLocalCallContext { + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } + } + + private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Typ = Unit; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + Typ getTyp(DataFlowType t) { any() } + + bindingset[c, t, tail] + Ap apCons(Content c, Typ t, Ap tail) { + result = true and exists(c) and exists(t) and exists(tail) + } + + class ApHeadContent = Unit; + + pragma[inline] + ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } + + ApHeadContent projectToHeadContent(Content c) { any() } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext + + bindingset[node1, state1] + bindingset[node2, state2] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2) and + state1 = state2 + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2) and + state1 = state2 + or + preservesValue = false and + additionalLocalStateStep(node1, state1, node2, state2) + ) and + exists(t) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + predicate flowIntoCall = flowIntoCallNodeCand1/4; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::revFlowIsReadAndStored(c) and + expectsContentEx(node, c) + ) + } + + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + PrevStage::revFlowState(state) and + t0 = t and + exists(ap) and + not stateBarrier(node, state) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node) + ) + } + + bindingset[typ, contentType] + predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } + } + + private module Stage2 implements StageSig { + import MkStage::Stage + } + + pragma[nomagic] + private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow + ) { + flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and + Stage2::revFlow(node2) and + Stage2::revFlow(node1) + } + + pragma[nomagic] + private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow + ) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and + Stage2::revFlow(node2) and + Stage2::revFlow(node1) + } + + private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) or + Config::neverSkip(this.asNode()) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state) { + Stage2::revFlow(node, state) and + ( + sourceNode(node, state) + or + jumpStepEx(_, node) + or + additionalJumpStep(_, node) + or + additionalJumpStateStep(_, _, node, state) + or + node instanceof ParamNodeEx + or + node.asNode() instanceof OutNodeExt + or + Stage2::storeStepCand(_, _, _, node, _, _) + or + Stage2::readStepCand(_, _, node) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state) and + s != state + ) + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state) { + exists(NodeEx next | Stage2::revFlow(next, state) | + jumpStepEx(node, next) or + additionalJumpStep(node, next) or + flowIntoCallNodeCand2(_, node, next, _) or + flowOutOfCallNodeCand2(_, node, _, next, _) or + Stage2::storeStepCand(node, _, _, next, _, _) or + Stage2::readStepCand(node, _, next) + ) + or + exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | + additionalJumpStateStep(node, state, next, s) + or + additionalLocalStateStep(node, state, next, s) and + s != state + ) + or + Stage2::revFlow(node, state) and + node instanceof FlowCheckNode + or + sinkNode(node, state) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 + ) { + additionalLocalFlowStepNodeCand1(node1, node2) and + state1 = state2 and + Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and + Stage2::revFlow(node2, pragma[only_bind_into](state2), false) + or + additionalLocalStateStep(node1, state1, node2, state2) and + Stage2::revFlow(node1, state1, false) and + Stage2::revFlow(node2, state2, false) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + LocalCallContext cc + ) { + not isUnreachableInCall1(node2, cc) and + ( + localFlowEntry(node1, pragma[only_bind_into](state)) and + ( + localFlowStepNodeCand1(node1, node2) and + preservesValue = true and + t = node1.getDataFlowType() and // irrelevant dummy value + Stage2::revFlow(node2, pragma[only_bind_into](state)) + or + additionalLocalFlowStepNodeCand2(node1, state, node2, state) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCall1(node1, cc) + or + exists(NodeEx mid | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and + localFlowStepNodeCand1(mid, node2) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](state)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, state, mid, _, _, cc) and + additionalLocalFlowStepNodeCand2(mid, state, node2, state) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext callContext + ) { + localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and + localFlowExit(node2, state1) and + state1 = state2 + or + additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and + state1 != state2 and + preservesValue = false and + t = node2.getDataFlowType() and + callContext.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) + } + } + + private import LocalFlowBigStep + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; + + class Typ = DataFlowType; + + class Ap = ApproxAccessPathFront; + + class ApNil = ApproxAccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + Typ getTyp(DataFlowType t) { result = t } + + bindingset[c, t, tail] + Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } + + class ApHeadContent = ContentApprox; + + pragma[noinline] + ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } + + predicate projectToHeadContent = getContentApprox/1; + + class ApOption = ApproxAccessPathFrontOption; + + ApOption apNone() { result = TApproxAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } + + import BooleanCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + predicate flowIntoCall = flowIntoCallNodeCand2/4; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::readStepCand(_, c, _) and + expectsContentEx(node, c) and + c = ap.getAHead() + ) + } + + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + exists(state) and + // We can get away with not using type strengthening here, since we aren't + // going to use the tracked types in the construction of Stage 4 access + // paths. For Stage 4 and onwards, the tracked types must be consistent as + // the cons candidates including types are used to construct subsequent + // access path approximations. + t0 = t and + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap) + ) + } + + bindingset[typ, contentType] + predicate typecheckStore(Typ typ, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(typ, contentType) + } + } + + private module Stage3 implements StageSig { + import MkStage::Stage + } + + bindingset[node, t0] + private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { + if castingNodeEx(node) + then + exists(DataFlowType nt | nt = node.getDataFlowType() | + if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) + ) + else t = t0 + } + + private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; + + class Typ = DataFlowType; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } + + Typ getTyp(DataFlowType t) { result = t } + + bindingset[c, t, tail] + Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } + + class ApHeadContent = Content; + + pragma[noinline] + ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } + + ApHeadContent projectToHeadContent(Content c) { result = c } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + import BooleanCallContext + + pragma[nomagic] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and + PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and + PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and + exists(lcc) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, + boolean allowsFieldFlow + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and + PrevStage::revFlow(node1, pragma[only_bind_into](state), _) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and + PrevStage::revFlow(node1, pragma[only_bind_into](state), _) + ) + } + + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c) { + PrevStage::revFlow(node) and + clearsContentCached(node.asNode(), c) + } + + pragma[nomagic] + private predicate clearContent(NodeEx node, Content c) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and + c = cs.getAReadContent() and + clearSet(node, cs) + ) + } + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::readStepCand(_, c, _) and + expectsContentEx(node, c) and + c = ap.getHead() + ) + } + + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + exists(state) and + not clear(node, ap) and + strengthenType(node, t0, t) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap) + ) + } + + bindingset[typ, contentType] + predicate typecheckStore(Typ typ, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(typ, contentType) + } + } + + private module Stage4 implements StageSig { + import MkStage::Stage + } + + /** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ + private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { + exists(AccessPathFront apf | + Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and + Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, + apf, _) + ) + } + + /** + * Holds if a length 2 access path approximation with the head `c` is expected + * to be expensive. + */ + private predicate expensiveLen2unfolding(Content c) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + or + flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not forceHighPrecision(c) + ) + } + + private newtype TAccessPathApprox = + TNil() or + TConsNil(Content c, DataFlowType t) { + Stage4::consCand(c, t, TFrontNil()) and + not expensiveLen2unfolding(c) + } or + TConsCons(Content c1, DataFlowType t, Content c2, int len) { + Stage4::consCand(c1, t, TFrontHead(c2)) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(c1) + } or + TCons1(Content c, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(c) + } + + /** + * Conceptually a list of `Content`s where nested tails are also paired with a + * `DataFlowType`, but only the first two elements of the list and its length + * are tracked. If data flows from a source to a given node with a given + * `AccessPathApprox`, this indicates the sequence of dereference operations + * needed to get from the value in the node to the tracked object. The + * `DataFlowType`s indicate the types of the stored values. + */ + abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract Content getHead(); + + abstract int len(); + + abstract AccessPathFront getFront(); + + /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ + abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); + } + + private class AccessPathApproxNil extends AccessPathApprox, TNil { + override string toString() { result = "" } + + override Content getHead() { none() } + + override int len() { result = 0 } + + override AccessPathFront getFront() { result = TFrontNil() } + + override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } + } + + abstract private class AccessPathApproxCons extends AccessPathApprox { } + + private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private Content c; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(c, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override Content getHead() { result = c } + + override int len() { result = 1 } + + override AccessPathFront getFront() { result = TFrontHead(c) } + + override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { + head = c and typ = t and tail = TNil() + } + } + + private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private Content c1; + private DataFlowType t; + private Content c2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } + + override string toString() { + if len = 2 + then result = "[" + c1.toString() + ", " + c2.toString() + "]" + else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" + } + + override Content getHead() { result = c1 } + + override int len() { result = len } + + override AccessPathFront getFront() { result = TFrontHead(c1) } + + override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { + head = c1 and + typ = t and + ( + tail = TConsCons(c2, _, _, len - 1) + or + len = 2 and + tail = TConsNil(c2, _) + or + tail = TCons1(c2, len - 1) + ) + } + } + + private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private Content c; + private int len; + + AccessPathApproxCons1() { this = TCons1(c, len) } + + override string toString() { + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + } + + override Content getHead() { result = c } + + override int len() { result = len } + + override AccessPathFront getFront() { result = TFrontHead(c) } + + override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { + head = c and + ( + exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | + tail = TConsCons(c2, _, _, len - 1) + or + len = 2 and + tail = TConsNil(c2, _) + or + tail = TCons1(c2, len - 1) + ) + or + len = 1 and + Stage4::consCand(c, typ, TFrontNil()) and + tail = TNil() + ) + } + } + + private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + + private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } + } + + private module Stage5Param implements MkStage::StageParam { + private module PrevStage = Stage4; + + class Typ = DataFlowType; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + pragma[nomagic] + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + + Typ getTyp(DataFlowType t) { result = t } + + bindingset[c, t, tail] + Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } + + class ApHeadContent = Content; + + pragma[noinline] + ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } + + ApHeadContent projectToHeadContent(Content c) { result = c } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + import Level1CallContext + import LocalCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and + PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and + PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, + boolean allowsFieldFlow + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and + PrevStage::revFlow(node1, pragma[only_bind_into](state), _) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and + PrevStage::revFlow(node1, pragma[only_bind_into](state), _) + ) + } + + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + strengthenType(node, t0, t) and + exists(state) and + exists(ap) + } + + bindingset[typ, contentType] + predicate typecheckStore(Typ typ, DataFlowType contentType) { + compatibleTypes(typ, contentType) + } + } + + private module Stage5 = MkStage::Stage; + + pragma[nomagic] + private predicate nodeMayUseSummary0( + NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa + ) { + exists(AccessPathApprox apa0 | + Stage5::parameterMayFlowThrough(p, _) and + Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and + Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, + TAccessPathApproxSome(apa), _, apa0, _) + ) + } + + pragma[nomagic] + private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { + exists(ParamNodeEx p | + Stage5::parameterMayFlowThrough(p, apa) and + nodeMayUseSummary0(n, p, state, apa) + ) + } + + private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { + exists(AccessPathApprox apa | ap.getApprox() = apa | + Stage5::parameterMayFlowThrough(p, apa) and + Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and + Stage5::revFlow(p, state, _) + ) + } + + /** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ + abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); + } + + /** A summary context from which no flow summary can be generated. */ + private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } + } + + /** A summary context from which a flow summary can be generated. */ + private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState s; + private DataFlowType t; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + /** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ + private int count1to2unfold(AccessPathApproxCons1 apa) { + exists(Content c, int len | + c = apa.getHead() and + len = apa.len() and + result = + strictcount(DataFlowType t, AccessPathFront apf | + Stage5::consCand(c, t, + any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) + ) + ) + } + + private int countNodesUsingAccessPath(AccessPathApprox apa) { + result = + strictcount(NodeEx n, FlowState state | + Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) + ) + } + + /** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ + private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa) and + nodes = countNodesUsingAccessPath(apa) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) + } + + private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { + exists(Content head | + apa.isCons(head, t, tail) and + Stage5::consCand(head, t, tail) + ) + } + + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + + /** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ + private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { + if forceUnfold(apa) + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa) and + nodes = countNodesUsingAccessPath(apa) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) + } + + /** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ + private int countAps(AccessPathApprox apa) { + evalUnfold(apa, false) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) + or + evalUnfold(apa, false) and + result = count1to2unfold(apa) and + not expensiveLen1to2unfolding(apa) + or + evalUnfold(apa, true) and + result = countPotentialAps(apa) + } + + /** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ + language[monotonicAggregates] + private int countPotentialAps(AccessPathApprox apa) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = + strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) + } + + private newtype TAccessPath = + TAccessPathNil() or + TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false) and + head = apa.getHead() and + hasTail(apa, t, tail.getApprox()) + ) + } or + TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { + exists(AccessPathApproxCons apa, AccessPathApprox tail | + evalUnfold(apa, false) and + not expensiveLen1to2unfolding(apa) and + apa.len() = len and + hasTail(apa, t, tail) and + head1 = apa.getHead() and + head2 = tail.getHead() + ) + } or + TAccessPathCons1(Content head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false) and + expensiveLen1to2unfolding(apa) and + apa.len() = len and + head = apa.getHead() + ) + } + + private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap + ) { + // A PathNode is introduced by a source ... + Stage5::revFlow(node, state) and + sourceNode(node, state) and + sourceCallCtx(cc) and + sc instanceof SummaryCtxNone and + t = node.getDataFlowType() and + ap = TAccessPathNil() + or + // ... or a step from an existing PathNode to another node. + pathStep(_, node, state, cc, sc, t, ap) + } or + TPathNodeSink(NodeEx node, FlowState state) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.getNodeEx() and + state = sink.getState() + ) + } or + TPathNodeSourceGroup(string sourceGroup) { + exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) + } or + TPathNodeSinkGroup(string sinkGroup) { + exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) + } + + /** + * A list of `Content`s where nested tails are also paired with a + * `DataFlowType`. If data flows from a source to a given node with a given + * `AccessPath`, this indicates the sequence of dereference operations needed + * to get from the value in the node to the tracked object. The + * `DataFlowType`s indicate the types of the stored values. + */ + private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract Content getHead(); + + /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ + abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + } + + private class AccessPathNil extends AccessPath, TAccessPathNil { + override Content getHead() { none() } + + override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil() } + + override AccessPathApproxNil getApprox() { result = TNil() } + + override int length() { result = 0 } + + override string toString() { result = "" } + } + + private class AccessPathCons extends AccessPath, TAccessPathCons { + private Content head_; + private DataFlowType t; + private AccessPath tail_; + + AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } + + override Content getHead() { result = head_ } + + override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { + head = head_ and typ = t and tail = tail_ + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head_) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head_, t) and tail_ = TAccessPathNil() + or + result = TConsCons(head_, t, tail_.getHead(), this.length()) + or + result = TCons1(head_, this.length()) + } + + override int length() { result = 1 + tail_.length() } + + private string toStringImpl(boolean needsSuffix) { + tail_ = TAccessPathNil() and + needsSuffix = false and + result = head_.toString() + "]" + concat(" : " + ppReprType(t)) + or + result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | + result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | + result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } + } + + private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private Content head1; + private DataFlowType t; + private Content head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } + + override Content getHead() { result = head1 } + + override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { + head = head1 and + typ = t and + Stage5::consCand(head1, t, tail.getApprox()) and + tail.getHead() = head2 and + tail.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, t, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = + "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } + } + + private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private Content head_; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head_, len) } + + override Content getHead() { result = head_ } + + override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { + head = head_ and + Stage5::consCand(head_, typ, tail.getApprox()) and + tail.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head_) } + + override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head_.toString() + "]" + else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" + } + } + + abstract private class PathNodeImpl extends TPathNode { + /** Gets the `FlowState` of this node. */ + abstract FlowState getState(); + + /** Holds if this node is a source. */ + abstract predicate isSource(); + + abstract PathNodeImpl getASuccessorImpl(); + + private PathNodeImpl getASuccessorIfHidden() { + this.isHidden() and + result = this.getASuccessorImpl() + } + + pragma[nomagic] + private PathNodeImpl getANonHiddenSuccessor0() { + result = this.getASuccessorIfHidden*() and + not result.isHidden() + } + + final PathNodeImpl getANonHiddenSuccessor() { + result = this.getASuccessorImpl().getANonHiddenSuccessor0() and + not this.isHidden() + } + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + not Config::includeHiddenNodes() and + ( + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + ) + } + + string getSourceGroup() { + this.isSource() and + Config::sourceGrouping(this.getNodeEx().asNode(), result) + } + + predicate isFlowSource() { + this.isSource() and not exists(this.getSourceGroup()) + or + this instanceof PathNodeSourceGroup + } + + predicate isFlowSink() { + this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or + this instanceof PathNodeSinkGroup + } + + private string ppType() { + this instanceof PathNodeSink and result = "" + or + exists(DataFlowType t | t = this.(PathNodeMid).getType() | + // The `concat` becomes "" if `ppReprType` has no result. + result = concat(" : " + ppReprType(t)) + ) + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + /** Holds if `n` can reach a sink. */ + private predicate directReach(PathNodeImpl n) { + n instanceof PathNodeSink or + n instanceof PathNodeSinkGroup or + directReach(n.getANonHiddenSuccessor()) + } + + /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ + private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } + + /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { + n1.getANonHiddenSuccessor() = n2 and directReach(n2) + } + + private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) + + /** + * A `Node` augmented with a call context (except for sinks) and an access path. + * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. + */ + class PathNode instanceof PathNodeImpl { + PathNode() { reach(this) } + + /** Gets a textual representation of this element. */ + final string toString() { result = super.toString() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + final string toStringWithContext() { result = super.toStringWithContext() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + final predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { super.getNodeEx().projectToNode() = result } + + /** Gets the `FlowState` of this node. */ + final FlowState getState() { result = super.getState() } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } + + /** Holds if this node is a source. */ + final predicate isSource() { super.isSource() } + + /** Holds if this node is a grouping of source nodes. */ + final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } + + /** Holds if this node is a grouping of sink nodes. */ + final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph implements PathGraphSig { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + key = "semmle.label" and val = n.toString() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Subpaths::subpaths(arg, par, ret, out) + } + } + + /** + * An intermediate flow graph node. This is a tuple consisting of a `Node`, + * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. + */ + private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + CallContext cc; + SummaryCtx sc; + DataFlowType t; + AccessPath ap; + + PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + DataFlowType getType() { result = t } + + AccessPath getAp() { result = ap } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx(), result.getType(), result.getAp()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink + result = this.getSuccMid().projectToSink() + } + + override predicate isSource() { + sourceNode(node, state) and + sourceCallCtx(cc) and + sc instanceof SummaryCtxNone and + t = node.getDataFlowType() and + ap = TAccessPathNil() + } + + predicate isAtSink() { + sinkNode(node, state) and + ap instanceof AccessPathNil and + if hasSinkCallCtx() + then + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. This also implies + // `sc instanceof SummaryCtxNone`. + // For `FeatureEqualSourceSinkCallContext` the initial call context was + // set to `CallContextSomeCall` and jumps are disallowed, so + // `cc instanceof CallContextNoCall` never holds. On the other hand, + // in this case there's never any need to enter a call except to identify + // a summary, so the condition in `pathIntoCallable` enforces this, which + // means that `sc instanceof SummaryCtxNone` holds if and only if we are + // in the call context of the source. + sc instanceof SummaryCtxNone or + cc instanceof CallContextNoCall + else any() + } + + PathNodeSink projectToSink() { + this.isAtSink() and + result.getNodeEx() = node and + result.getState() = state + } + } + + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ + private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; + + PathNodeSink() { this = TPathNodeSink(node, state) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } + + override predicate isSource() { sourceNode(node, state) } + + string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } + } + + private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { + string sourceGroup; + + PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } + + override predicate isSource() { none() } + + override string toString() { result = sourceGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } + } + + private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { + string sinkGroup; + + PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } + + override NodeEx getNodeEx() { none() } + + override FlowState getState() { none() } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { none() } + + override string toString() { result = sinkGroup } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 + } + } + + private predicate pathNode( + PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, + DataFlowType t, AccessPath ap, LocalCallContext localCC + ) { + midnode = mid.getNodeEx() and + state = mid.getState() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + t = mid.getType() and + ap = mid.getAp() + } + + private predicate pathStep( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, + AccessPath ap + ) { + exists(DataFlowType t0 | + pathStep0(mid, node, state, cc, sc, t0, ap) and + Stage5::revFlow(node, state, ap.getApprox()) and + strengthenType(node, t0, t) + ) + } + + /** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ + pragma[nomagic] + private predicate pathStep0( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, + AccessPath ap + ) { + exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and + localFlowBigStep(midnode, state0, node, state, true, _, localCC) + ) + or + exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and + localFlowBigStep(midnode, state0, node, state, false, t, localCC) and + ap instanceof AccessPathNil + ) + or + jumpStepEx(mid.getNodeEx(), node) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + t = mid.getType() and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + t = node.getDataFlowType() and + ap = TAccessPathNil() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + t = node.getDataFlowType() and + ap = TAccessPathNil() + or + exists(Content c, DataFlowType t0, AccessPath ap0 | + pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and + ap.isCons(c, t0, ap0) and + sc = mid.getSummaryCtx() + ) + or + exists(Content c, AccessPath ap0 | + pathReadStep(mid, node, state, ap0, c, cc) and + ap0.isCons(c, t, ap) and + sc = mid.getSummaryCtx() + ) + or + pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() + or + pathOutOfCallable(mid, node, state, cc) and + t = mid.getType() and + ap = mid.getAp() and + sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() + } + + pragma[nomagic] + private predicate pathReadStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc + ) { + ap0 = mid.getAp() and + c = ap0.getHead() and + Stage5::readStepCand(mid.getNodeEx(), c, node) and + state = mid.getState() and + cc = mid.getCallContext() + } + + pragma[nomagic] + private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, + DataFlowType t, CallContext cc + ) { + exists(DataFlowType contentType | + t0 = mid.getType() and + ap0 = mid.getAp() and + Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and + state = mid.getState() and + cc = mid.getCallContext() and + compatibleTypes(t0, contentType) + ) + } + + private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, + AccessPathApprox apa + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() + } + + pragma[nomagic] + private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPathApprox apa + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, state, innercc, apa) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + pragma[noinline] + private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { + result.asNode() = kind.getAnOutNode(call) and + Stage5::revFlow(result, _, apa) + } + + /** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ + pragma[noinline] + private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | + pathOutOfCallable1(mid, call, kind, state, cc, apa) and + out = getAnOutNodeFlow(kind, call, apa) + ) + } + + /** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ + pragma[noinline] + private predicate pathIntoArg( + PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, + DataFlowType t, AccessPath ap, AccessPathApprox apa + ) { + exists(ArgNodeEx arg, ArgumentPosition apos | + pathNode(mid, arg, state, cc, _, t, ap, _) and + arg.asNode().(ArgNode).argumentOf(call, apos) and + apa = ap.getApprox() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate parameterCand( + DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa + ) { + exists(ParamNodeEx p | + Stage5::revFlow(p, _, apa) and + p.isParameterOf(callable, pos) + ) + } + + pragma[nomagic] + private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap + ) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, + pragma[only_bind_into](apa)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) + ) + } + + /** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ + pragma[nomagic] + private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, + SummaryCtx sc, DataFlowCall call + ) { + exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | + pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and + p.isParameterOf(callable, pos) and + ( + sc = TSummaryCtxSome(p, state, t, ap) + or + not exists(TSummaryCtxSome(p, state, t, ap)) and + sc = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ + pragma[nomagic] + private predicate paramFlowsThrough( + ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, + AccessPath ap, AccessPathApprox apa + ) { + exists(RetNodeEx ret | + pathNode(_, ret, state, cc, sc, t, ap, _) and + kind = ret.getKind() and + apa = ap.getApprox() and + parameterFlowThroughAllowed(sc.getParamNode(), kind) + ) + } + + pragma[nomagic] + private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, + DataFlowType t, AccessPath ap, AccessPathApprox apa + ) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, _, cc, innercc, sc, call) and + paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) + ) + } + + /** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ + pragma[noinline] + private predicate pathThroughCallable( + PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap + ) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa) + ) + } + + private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, + ReturnKindExt kind, NodeEx out, FlowState sout, DataFlowType t, AccessPath apout + ) { + pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), + pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, _, innercc, sc, _) and + paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, + pragma[only_bind_into](t), pragma[only_bind_into](apout), _) and + not arg.isHidden() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `sout`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, + ReturnKindExt kind, NodeEx out, FlowState sout, DataFlowType t, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, + DataFlowType t, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and + pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and + kind = retnode.getKind() + ) + } + + private PathNodeImpl localStepToHidden(PathNodeImpl n) { + n.getASuccessorImpl() = result and + result.isHidden() and + exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | + localFlowBigStep(n1, _, n2, _, _, _, _) or + storeEx(n1, _, n2, _, _) or + readSetEx(n1, _, n2) + ) + } + + pragma[nomagic] + private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { + succ = pred.getANonHiddenSuccessor() and + succNode = succ.getNodeEx() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { + exists( + ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, + PathNodeMid out0 + | + pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and + subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and + hasSuccessor(pragma[only_bind_into](arg), par, p) and + not ret.isHidden() and + pathNode(out0, o, sout, _, _, t, apout, _) + | + out = out0 or out = out0.projectToSink() + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + predicate retReach(PathNodeImpl n) { + exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } + } + + /** + * Holds if data can flow from `source` to `sink`. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate flowPath(PathNode source, PathNode sink) { + exists(PathNodeImpl flowsource, PathNodeImpl flowsink | + source = flowsource and sink = flowsink + | + flowsource.isFlowSource() and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isFlowSink() + ) + } + + /** DEPRECATED: Use `flowPath` instead. */ + deprecated predicate hasFlowPath = flowPath/2; + + private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { + flowsource.isSource() and + flowsource.getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink + } + + /** + * Holds if data can flow from `source` to `sink`. + */ + predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } + + /** DEPRECATED: Use `flow` instead. */ + deprecated predicate hasFlow = flow/2; + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } + + /** DEPRECATED: Use `flowTo` instead. */ + deprecated predicate hasFlowTo = flowTo/1; + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } + + /** DEPRECATED: Use `flowToExpr` instead. */ + deprecated predicate hasFlowToExpr = flowToExpr/1; + + private predicate finalStats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples + ) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and + tuples = count(PathNodeImpl pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and + tuples = count(PathNode pn) + } + + /** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ + predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples + ) { + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples) + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples) + or + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples) + or + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples) + or + stage = "5 Fwd" and + n = 50 and + Stage5::stats(true, nodes, fields, conscand, states, tuples) + or + stage = "5 Rev" and + n = 55 and + Stage5::stats(false, nodes, fields, conscand, states, tuples) + or + stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) + or + stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) + } + + module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { + exists(NodeEx node1, NodeEx node2 | + jumpStepEx(node1, node2) + or + additionalJumpStep(node1, node2) + or + additionalJumpStateStep(node1, _, node2, _) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c) { + exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) + } + + private predicate interestingCallableSink(DataFlowCallable c) { + exists(Node n | c = getNodeEnclosingCallable(n) | + Config::isSink(n, _) or + Config::isSink(n) + ) + or + exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c) { + interestingCallableSrc(c) or + interestingCallableSink(c) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2 | + callableStep(c1, c2) and + ce1 = TCallable(c1) and + ce2 = TCallable(c2) + ) + or + exists(Node n | + ce1 = TCallableSrc() and + Config::isSource(n, _) and + ce2 = TCallable(getNodeEnclosingCallable(n)) + ) + or + exists(Node n | + ce2 = TCallableSink() and + ce1 = TCallable(getNodeEnclosingCallable(n)) + | + Config::isSink(n, _) or + Config::isSink(n) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } + + private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } + + private newtype TPartialAccessPath = + TPartialNil() or + TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil() and result = 0 + or + this = TPartialCons(_, result) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { result = "" } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(Content c, int len | this = TPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state) or + sinkNodeWithState(_, state) or + additionalLocalStateStep(_, state, _, _) or + additionalLocalStateStep(_, _, _, state) or + additionalJumpStateStep(_, state, _, _) or + additionalJumpStateStep(_, _, _, state) + } + + private predicate revSinkNode(NodeEx node, FlowState state) { + sinkNodeWithState(node, state) + or + Config::isSink(node.asNode()) and + relevantState(state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(DataFlowType t) + + private newtype TSummaryCtx4 = + TSummaryCtx4None() or + TSummaryCtx4Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(PartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + sourceNode(node, state) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + t = node.getDataFlowType() and + ap = TPartialNil() and + exists(explorationLimit()) + or + partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and + distSrc(node.getEnclosingCallable()) <= explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, PartialAccessPath ap + ) { + revSinkNode(node, state) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TPartialNil() and + exists(explorationLimit()) + or + revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node) and + not stateBarrier(node, state) and + distSink(node.getEnclosingCallable()) <= explorationLimit() + } + + pragma[nomagic] + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) + } + + pragma[nomagic] + private predicate partialPathStep1( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, + PartialAccessPath ap + ) { + partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and + not fullBarrier(node) and + not stateBarrier(node, state) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + strengthenType(node, t0, t) + } + + pragma[nomagic] + private predicate partialPathTypeStrengthen( + DataFlowType t0, PartialAccessPath ap, DataFlowType t + ) { + partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } + + private string ppType() { + this instanceof PartialPathNodeRev and result = "" + or + exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | + // The `concat` becomes "" if `ppReprType` has no result. + result = concat(" : " + ppReprType(t)) + ) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + TSummaryCtx4 sc4; + DataFlowType t; + PartialAccessPath ap; + + PartialPathNodeFwd() { + this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) + } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + TSummaryCtx4 getSummaryCtx4() { result = sc4 } + + DataFlowType getType() { result = t } + + PartialAccessPath getAp() { result = ap } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), + result.getSummaryCtx4(), result.getType(), result.getAp()) + } + + predicate isSource() { + sourceNode(node, state) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + PartialAccessPath ap; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) + } + + predicate isSink() { + revSinkNode(node, state) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TPartialNil() + } + } + + pragma[nomagic] + private predicate partialPathStep0( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStepEx(mid.getNodeEx(), node) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + t = mid.getType() and + ap = mid.getAp() + or + additionalLocalFlowStep(mid.getNodeEx(), node) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + ) + or + jumpStepEx(mid.getNodeEx(), node) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + t = mid.getType() and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + or + partialPathStoreStep(mid, _, _, _, node, t, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() + or + exists(DataFlowType t0, PartialAccessPath ap0, Content c | + partialPathReadStep(mid, t0, ap0, c, node, cc) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + apConsFwd(t, ap, c, t0, ap0) + ) + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) + or + partialPathOutOfCallable(mid, node, state, cc, t, ap) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() + or + partialPathThroughCallable(mid, node, state, cc, t, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, + DataFlowType t2, PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + t1 = mid.getType() and + ap1 = mid.getAp() and + storeEx(midNode, c, node, contentType, t2) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(t1, contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 + ) { + partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) + or + exists(DataFlowType t0 | + partialPathTypeStrengthen(t0, ap2, t2) and + apConsFwd(t1, ap1, c, t0, ap2) + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, + CallContext cc + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + t = mid.getType() and + ap = mid.getAp() and + read(midNode, c, node) and + ap.getHead() = c and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + DataFlowType t, PartialAccessPath ap + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + t = mid.getType() and + ap = mid.getAp() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, + CallContext cc, DataFlowType t, PartialAccessPath ap + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) + then cc = TReturn(c, call) + else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, + PartialAccessPath ap + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + t = mid.getType() and + ap = mid.getAp() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(t) and + sc4 = TSummaryCtx4Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + t = mid.getType() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, + CallContext cc, DataFlowType t, PartialAccessPath ap + ) { + exists( + CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + TSummaryCtx4 sc4 + | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, + PartialAccessPath ap + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, + TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap + ) { + localFlowStepEx(node, mid.getNodeEx()) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() + or + additionalLocalFlowStep(node, mid.getNodeEx()) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() + or + jumpStepEx(node, mid.getNodeEx()) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() + or + additionalJumpStep(node, mid.getNodeEx()) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + or + exists(PartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, state, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { + revPartialPathReadStep(_, ap1, c, _, ap2) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + storeEx(node, c, midNode, _, _) and + ap.getHead() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, PartialAccessPath ap + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + PartialAccessPath ap + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } + + private predicate partialFlow(PartialPathNode source, PartialPathNode node) { + source.isFwdSource() and + node = source.getASuccessor+() + } + + private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { + sink.isRevSink() and + node.getASuccessor+() = sink + } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink) and + dist = node.getSinkDistance() + } + } + } +} diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll new file mode 100644 index 00000000000..27528918b6a --- /dev/null +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -0,0 +1,1488 @@ +private import codeql.dataflow.DataFlow + +module MakeImplCommon { + private import Lang + import Cached + + module DataFlowImplCommonPublic { + /** Provides `FlowState = string`. */ + module FlowStateString { + /** A state value to track during data flow. */ + class FlowState = string; + + /** + * The default state, which is used when the state is unspecified for a source + * or a sink. + */ + class FlowStateEmpty extends FlowState { + FlowStateEmpty() { this = "" } + } + } + + private newtype TFlowFeature = + TFeatureHasSourceCallContext() or + TFeatureHasSinkCallContext() or + TFeatureEqualSourceSinkCallContext() + + /** A flow configuration feature for use in `Configuration::getAFeature()`. */ + class FlowFeature extends TFlowFeature { + string toString() { none() } + } + + /** + * A flow configuration feature that implies that sources have some existing + * call context. + */ + class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { + override string toString() { result = "FeatureHasSourceCallContext" } + } + + /** + * A flow configuration feature that implies that sinks have some existing + * call context. + */ + class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { + override string toString() { result = "FeatureHasSinkCallContext" } + } + + /** + * A flow configuration feature that implies that source-sink pairs have some + * shared existing call context. + */ + class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { + override string toString() { result = "FeatureEqualSourceSinkCallContext" } + } + } + + /** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ + predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 + } + + /** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ + predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 + } + + /** + * Holds if `arg` is an argument of `call` with an argument position that matches + * parameter position `ppos`. + */ + pragma[noinline] + predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { + exists(ArgumentPosition apos | + arg.argumentOf(call, apos) and + parameterMatch(ppos, apos) + ) + } + + /** + * Provides a simple data-flow analysis for resolving lambda calls. The analysis + * currently excludes read-steps, store-steps, and flow-through. + * + * The analysis uses non-linear recursion: When computing a flow path in or out + * of a call, we use the results of the analysis recursively to resolve lambda + * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. + */ + private module LambdaFlow { + pragma[noinline] + private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallable(call), ppos) + } + + pragma[noinline] + private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallableLambda(call, _), ppos) + } + + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParamNonLambda(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) + ) + } + + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParamLambda(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) + ) + } + + private newtype TReturnPositionSimple = + TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { + exists(ReturnNode ret | + c = getNodeEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + pragma[noinline] + private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { + result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { + result = TReturnPositionSimple0(viableCallable(call), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { + result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) + } + + private predicate viableReturnPosOutNonLambda( + DataFlowCall call, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosNonLambda(call, kind) and + out = getAnOutNode(call, kind) + ) + } + + pragma[nomagic] + private predicate viableReturnPosOutLambda( + DataFlowCall call, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosLambda(call, kind) and + out = getAnOutNode(call, kind) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to + * the lambda call `lambdaCall`. + * + * The parameter `toReturn` indicates whether the path from `node` to + * `lambdaCall` goes through a return, and `toJump` whether the path goes + * through a jump step. + * + * The call context `lastCall` records the last call on the path from `node` + * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing + * callable of `lambdaCall`. + */ + pragma[nomagic] + predicate revLambdaFlow( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and + not expectsContent(node, _) and + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + predicate revLambdaFlow0( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + lambdaCall(lambdaCall, kind, node) and + t = getNodeDataFlowType(node) and + toReturn = false and + toJump = false and + lastCall = TDataFlowCallNone() + or + // local flow + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) + | + simpleLocalFlowStep(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // jump step + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and + toReturn = false and + toJump = true + | + jumpStepCached(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // flow into a callable + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | + revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and + ( + if lastCall0 = TDataFlowCallNone() and toJump = false + then lastCall = TDataFlowCallSome(call) + else lastCall = lastCall0 + ) and + toReturn = false + | + viableParamArgNonLambda(call, p, node) + or + viableParamArgLambda(call, p, node) // non-linear recursion + ) + or + // flow out of a callable + exists(TReturnPositionSimple pos | + revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and + getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and + toReturn = true + ) + } + + pragma[nomagic] + predicate revLambdaFlowOutLambdaCall( + DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, + DataFlowCall call, DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + exists(ReturnKindExt rk | + out = rk.getAnOutNode(call) and + lambdaCall(call, _, _) + ) + } + + pragma[nomagic] + predicate revLambdaFlowOut( + DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall + ) { + exists(DataFlowCall call, OutNode out | + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + viableReturnPosOutNonLambda(call, pos, out) + or + // non-linear recursion + revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and + viableReturnPosOutLambda(call, pos, out) + ) + } + + pragma[nomagic] + predicate revLambdaFlowIn( + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) + } + } + + private DataFlowCallable viableCallableExt(DataFlowCall call) { + result = viableCallable(call) + or + result = viableCallableLambda(call, _) + } + + cached + private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, ParameterPosition pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + readSet(_, _, n) + } + + cached + predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { + isParameterNode(p, c, pos) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { + isArgumentNode(n, call, pos) + } + + /** + * Gets a viable target for the lambda call `call`. + * + * `lastCall` records the call required to reach `call` in order for the result + * to be a viable target, if any. + */ + cached + DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { + exists(Node creation, LambdaCallKind kind | + LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and + lambdaCreation(creation, kind, result) + ) + } + + /** + * Holds if `p` is the parameter of a viable dispatch target of `call`, + * and `p` has position `ppos`. + */ + pragma[nomagic] + private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallableExt(call), ppos) + } + + /** + * Holds if `arg` is a possible argument to `p` in `call`, taking virtual + * dispatch into account. + */ + cached + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParam(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and + golangSpecificParamArgFilter(call, p, arg) + ) + } + + pragma[nomagic] + private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { + viableCallableExt(call) = result.getCallable() and + kind = result.getKind() + } + + /** + * Holds if a value at return position `pos` can be returned to `out` via `call`, + * taking virtual dispatch into account. + */ + cached + predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { + exists(ReturnKindExt kind | + pos = viableReturnPos(call, kind) and + out = kind.getAnOutNode(call) + ) + } + + /** Provides predicates for calculating flow-through summaries. */ + private module FlowThrough { + /** + * The first flow-through approximation: + * + * - Input access paths are abstracted with a Boolean parameter + * that indicates (non-)emptiness. + */ + private module Cand { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps. + * + * `read` indicates whether it is contents of `p` that can flow to `node`. + */ + pragma[nomagic] + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { + p = node and + read = false + or + // local flow + exists(Node mid | + parameterValueFlowCand(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlowCand(p, mid, false) and + readSet(mid, _, node) and + read = true + ) + or + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, false) and + argumentValueFlowsThroughCand(arg, node, read) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, read) and + argumentValueFlowsThroughCand(arg, node, false) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { + parameterValueFlowCand(p, arg, read) + } + + pragma[nomagic] + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { + parameterValueFlowCand(p, n.getPreUpdateNode(), false) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps, not taking call contexts + * into account. + * + * `read` indicates whether it is contents of `p` that can flow to the return + * node. + */ + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { + exists(ReturnNode ret | + parameterValueFlowCand(p, ret, read) and + kind = ret.getKind() + ) + } + + pragma[nomagic] + private predicate argumentValueFlowsThroughCand0( + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturnCand(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only value-preserving steps, + * not taking call contexts into account. + * + * `read` indicates whether it is contents of `arg` that can flow to `out`. + */ + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThroughCand0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + ) + } + + predicate cand(ParamNode p, Node n) { + parameterValueFlowCand(p, n, _) and + ( + parameterValueFlowReturnCand(p, _, _) + or + parameterValueFlowsToPreUpdateCand(p, _) + ) + } + } + + /** + * The final flow-through calculation: + * + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. + * - Types are checked using the `compatibleTypes()` relation. + */ + private module Final { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and + if node instanceof CastingNode + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) + or + // getter + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { + p = node and + Cand::cand(p, _) and + read = TReadStepTypesNone() + or + // local flow + exists(Node mid | + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and + Cand::parameterValueFlowReturnCand(p, _, true) and + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) + ) + or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read + ) { + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { + parameterValueFlow(p, arg, read) and + Cand::argumentValueFlowsThroughCand(arg, _, _) + } + + pragma[nomagic] + private predicate argumentValueFlowsThrough0( + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturn(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + pragma[nomagic] + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) + or + // getter + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + * + * This predicate is exposed for testing only. + */ + predicate getterStep(ArgNode arg, ContentSet c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps and possibly a single read + * step. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + private predicate parameterValueFlowReturn( + ParamNode p, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ReturnNode ret | + parameterValueFlow(p, ret, read) and + kind = ret.getKind() + ) + } + } + + import Final + } + + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. + */ + pragma[nomagic] + private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { + mayBenefitFromCallContext(call, callable) + or + callEnclosingCallable(call, callable) and + exists(viableCallableLambda(call, TDataFlowCallSome(_))) + } + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ + cached + DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + result = viableCallable(call) + or + result = viableCallableLambda(call, TDataFlowCallSome(ctx)) + or + exists(DataFlowCallable enclosing | + mayBenefitFromCallContextExt(call, enclosing) and + enclosing = viableCallableExt(ctx) and + result = viableCallableLambda(call, TDataFlowCallNone()) + ) + } + + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext( + DataFlowCall call, DataFlowCallable c, DataFlowCall ctx + ) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, c) and + c = viableCallableExt(ctx) and + ctxtgts = count(viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(viableCallableExt(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, _) and + c = viableCallableExt(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + + /** + * Holds if `p` can flow to the pre-update node associated with post-update + * node `n`, in the same callable, using only value-preserving steps. + */ + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) + } + + cached + predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } + + cached + predicate storeSet( + Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) + or + readSet(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `c`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + exists(ContentSet cs | + c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) + ) + } + + /** + * Holds if data can flow from `fromNode` to `toNode` because they are the post-update + * nodes of some function output and input respectively, where the output and input + * are aliases. A typical example is a function returning `this`, implementing a fluent + * interface. + */ + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { + exists(Node fromPre, Node toPre | + fromPre = fromNode.getPreUpdateNode() and + toPre = toNode.getPreUpdateNode() + | + exists(DataFlowCall c | + // Does the language-specific simpleLocalFlowStep already model flow + // from function input to output? + fromPre = getAnOutNode(c, _) and + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) + ) + or + argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) + ) + } + + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + + /** + * Holds if the call context `call` improves virtual dispatch in `callable`. + */ + cached + predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { + reducedViableImplInCallContext(_, callable, call) + } + + /** + * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. + */ + cached + predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) + } + + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + + cached + newtype TCallContext = + TAnyCallContext() or + TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or + TSomeCall() or + TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } + + cached + newtype TReturnPosition = + TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { + exists(ReturnNodeExt ret | + c = returnNodeGetEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + cached + newtype TLocalFlowCallContext = + TAnyLocalCall() or + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } + + cached + newtype TReturnKindExt = + TValueReturn(ReturnKind kind) or + TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } + + cached + newtype TBooleanOption = + TBooleanNone() or + TBooleanSome(boolean b) { b = true or b = false } + + cached + newtype TDataFlowCallOption = + TDataFlowCallNone() or + TDataFlowCallSome(DataFlowCall call) + + cached + newtype TParamNodeOption = + TParamNodeNone() or + TParamNodeSome(ParamNode p) + + cached + newtype TReturnCtx = + TReturnCtxNone() or + TReturnCtxNoFlowThrough() or + TReturnCtxMaybeFlowThrough(ReturnPosition pos) + + cached + newtype TAccessPathFront = + TFrontNil() or + TFrontHead(Content c) + + cached + newtype TApproxAccessPathFront = + TApproxFrontNil() or + TApproxFrontHead(ContentApprox c) + + cached + newtype TAccessPathFrontOption = + TAccessPathFrontNone() or + TAccessPathFrontSome(AccessPathFront apf) + + cached + newtype TApproxAccessPathFrontOption = + TApproxAccessPathFrontNone() or + TApproxAccessPathFrontSome(ApproxAccessPathFront apf) + } + + /** + * Holds if the call context `call` either improves virtual dispatch in + * `callable` or if it allows us to prune unreachable nodes in `callable`. + */ + predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { + recordDataFlowCallSiteDispatch(call, callable) or + recordDataFlowCallSiteUnreachable(call, callable) + } + + /** + * A `Node` at which a cast can occur such that the type should be checked. + */ + class CastingNode instanceof Node { + CastingNode() { castingNode(this) } + + string toString() { result = super.toString() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + private predicate readStepWithTypes( + Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content + ) { + readSet(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) + } + + private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } + + private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } + } + + /** + * A call context to restrict the targets of virtual dispatch, prune local flow, + * and match the call sites of flow into a method with flow out of a method. + * + * There are four cases: + * - `TAnyCallContext()` : No restrictions on method flow. + * - `TSpecificCall(DataFlowCall call)` : Flow entered through the + * given `call`. This call improves the set of viable + * dispatch targets for at least one method call in the current callable + * or helps prune unreachable nodes in the current callable. + * - `TSomeCall()` : Flow entered through a parameter. The + * originating call does not improve the set of dispatch targets for any + * method call in the current callable and was therefore not recorded. + * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and + * this dispatch target of `call` implies a reduced set of dispatch origins + * to which data may flow if it should reach a `return` statement. + */ + abstract class CallContext extends TCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); + } + + abstract class CallContextNoCall extends CallContext { } + + class CallContextAny extends CallContextNoCall, TAnyCallContext { + override string toString() { result = "CcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } + } + + abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); + } + + class CallContextSpecificCall extends CallContextCall, TSpecificCall { + override string toString() { + exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + recordDataFlowCallSite(this.getCall(), callable) + } + + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + + DataFlowCall getCall() { this = TSpecificCall(result) } + } + + class CallContextSomeCall extends CallContextCall, TSomeCall { + override string toString() { result = "CcSomeCall" } + + override predicate relevantFor(DataFlowCallable callable) { + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) + } + + override predicate matchesCall(DataFlowCall call) { any() } + } + + class CallContextReturn extends CallContextNoCall, TReturn { + override string toString() { + exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) + } + } + + /** + * A call context that is relevant for pruning local flow. + */ + abstract class LocalCallContext extends TLocalFlowCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); + } + + class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { + override string toString() { result = "LocalCcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } + } + + class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { + LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } + + DataFlowCall call; + + DataFlowCall getCall() { result = call } + + override string toString() { result = "LocalCcCall(" + call + ")" } + + override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } + } + + private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) + } + + /** + * Gets the local call context given the call context and the callable that + * the contexts apply to. + */ + LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { + ctx.relevantFor(callable) and + if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) + then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() + else result instanceof LocalCallContextAny + } + + /** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ + class ParamNode instanceof Node { + ParamNode() { parameterNode(this, _, _) } + + string toString() { result = super.toString() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * position. + */ + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + parameterNode(this, c, pos) + } + } + + /** A data-flow node that represents a call argument. */ + class ArgNode instanceof Node { + ArgNode() { argumentNode(this, _, _) } + + string toString() { result = super.toString() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + argumentNode(this, call, pos) + } + } + + /** + * A node from which flow can return to the caller. This is either a regular + * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. + */ + class ReturnNodeExt instanceof Node { + ReturnNodeExt() { returnNodeExt(this, _) } + + string toString() { result = super.toString() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the kind of this returned value. */ + ReturnKindExt getKind() { returnNodeExt(this, result) } + } + + /** + * A node to which data can flow from a call. Either an ordinary out node + * or a post-update node associated with a call argument. + */ + class OutNodeExt instanceof Node { + OutNodeExt() { outNodeExt(this) } + + string toString() { result = super.toString() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + /** + * An extended return kind. A return kind describes how data can be returned + * from a callable. This can either be through a returned value or an updated + * parameter. + */ + abstract class ReturnKindExt extends TReturnKindExt { + /** Gets a textual representation of this return kind. */ + abstract string toString(); + + /** Gets a node corresponding to data flow out of `call`. */ + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } + } + + class ValueReturnKind extends ReturnKindExt, TValueReturn { + private ReturnKind kind; + + ValueReturnKind() { this = TValueReturn(kind) } + + ReturnKind getKind() { result = kind } + + override string toString() { result = kind.toString() } + } + + class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { + private ParameterPosition pos; + + ParamUpdateReturnKind() { this = TParamUpdate(pos) } + + ParameterPosition getPosition() { result = pos } + + pragma[nomagic] + ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } + + override string toString() { result = "param update " + pos } + } + + /** A callable tagged with a relevant return kind. */ + class ReturnPosition extends TReturnPosition0 { + private DataFlowCallable c; + private ReturnKindExt kind; + + ReturnPosition() { this = TReturnPosition0(c, kind) } + + /** Gets the callable. */ + DataFlowCallable getCallable() { result = c } + + /** Gets the return kind. */ + ReturnKindExt getKind() { result = kind } + + /** Gets a textual representation of this return position. */ + string toString() { result = "[" + kind + "] " + c } + } + + /** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ + pragma[inline] + DataFlowCallable getNodeEnclosingCallable(Node n) { + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) + } + + /** Gets the type of `n` used for type pruning. */ + pragma[inline] + DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) + } + + pragma[noinline] + private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { + result = getNodeEnclosingCallable(ret) + } + + pragma[noinline] + private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { + result.getCallable() = returnNodeGetEnclosingCallable(ret) and + kind = result.getKind() + } + + pragma[noinline] + ReturnPosition getReturnPosition(ReturnNodeExt ret) { + result = getReturnPosition0(ret, ret.getKind()) + } + + /** + * Checks whether `inner` can return to `call` in the call context `innercc`. + * Assumes a context of `inner = viableCallableExt(call)`. + */ + bindingset[innercc, inner, call] + predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { + innercc instanceof CallContextAny + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, inner) and + innercc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) + } + + /** + * Checks whether `call` can resolve to `calltarget` in the call context `cc`. + * Assumes a context of `calltarget = viableCallableExt(call)`. + */ + bindingset[cc, call, calltarget] + predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then calltarget = prunedViableImplInCallContext(call, ctx) + else any() + ) + or + cc instanceof CallContextSomeCall + or + cc instanceof CallContextAny + or + cc instanceof CallContextReturn + } + + /** + * Resolves a return from `callable` in `cc` to `call`. This is equivalent to + * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. + */ + bindingset[cc, callable] + predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { + cc instanceof CallContextAny and callable = viableCallableExt(call) + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, callable) and + cc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) + } + + /** + * Resolves a call from `call` in `cc` to `result`. This is equivalent to + * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. + */ + bindingset[call, cc] + DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then result = prunedViableImplInCallContext(call, ctx) + else result = viableCallableExt(call) + ) + or + result = viableCallableExt(call) and cc instanceof CallContextSomeCall + or + result = viableCallableExt(call) and cc instanceof CallContextAny + or + result = viableCallableExt(call) and cc instanceof CallContextReturn + } + + /** An optional Boolean value. */ + class BooleanOption extends TBooleanOption { + string toString() { + this = TBooleanNone() and result = "" + or + this = TBooleanSome(any(boolean b | result = b.toString())) + } + } + + /** An optional `DataFlowCall`. */ + class DataFlowCallOption extends TDataFlowCallOption { + string toString() { + this = TDataFlowCallNone() and + result = "(none)" + or + exists(DataFlowCall call | + this = TDataFlowCallSome(call) and + result = call.toString() + ) + } + } + + /** An optional `ParamNode`. */ + class ParamNodeOption extends TParamNodeOption { + string toString() { + this = TParamNodeNone() and + result = "(none)" + or + exists(ParamNode p | + this = TParamNodeSome(p) and + result = p.toString() + ) + } + } + + /** + * A return context used to calculate flow summaries in reverse flow. + * + * The possible values are: + * + * - `TReturnCtxNone()`: no return flow. + * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. + * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and + * flow through may be possible. + */ + class ReturnCtx extends TReturnCtx { + string toString() { + this = TReturnCtxNone() and + result = "(none)" + or + this = TReturnCtxNoFlowThrough() and + result = "(no flow through)" + or + exists(ReturnPosition pos | + this = TReturnCtxMaybeFlowThrough(pos) and + result = pos.toString() + ) + } + } + + /** + * The front of an approximated access path. This is either a head or a nil. + */ + abstract class ApproxAccessPathFront extends TApproxAccessPathFront { + abstract string toString(); + + abstract boolean toBoolNonEmpty(); + + ContentApprox getHead() { this = TApproxFrontHead(result) } + + pragma[nomagic] + Content getAHead() { + exists(ContentApprox cont | + this = TApproxFrontHead(cont) and + cont = getContentApprox(result) + ) + } + } + + class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { + override string toString() { result = "nil" } + + override boolean toBoolNonEmpty() { result = false } + } + + class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { + private ContentApprox c; + + ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } + + override string toString() { result = c.toString() } + + override boolean toBoolNonEmpty() { result = true } + } + + /** An optional approximated access path front. */ + class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { + string toString() { + this = TApproxAccessPathFrontNone() and result = "" + or + this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) + } + } + + /** + * The front of an access path. This is either a head or a nil. + */ + abstract class AccessPathFront extends TAccessPathFront { + abstract string toString(); + + abstract ApproxAccessPathFront toApprox(); + + Content getHead() { this = TFrontHead(result) } + } + + class AccessPathFrontNil extends AccessPathFront, TFrontNil { + override string toString() { result = "nil" } + + override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } + } + + class AccessPathFrontHead extends AccessPathFront, TFrontHead { + private Content c; + + AccessPathFrontHead() { this = TFrontHead(c) } + + override string toString() { result = c.toString() } + + override ApproxAccessPathFront toApprox() { result.getAHead() = c } + } + + /** An optional access path front. */ + class AccessPathFrontOption extends TAccessPathFrontOption { + string toString() { + this = TAccessPathFrontNone() and result = "" + or + this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) + } + } +} diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml new file mode 100644 index 00000000000..f8c2322f6a1 --- /dev/null +++ b/shared/dataflow/qlpack.yml @@ -0,0 +1,8 @@ +name: codeql/dataflow +version: 0.0.1 +groups: shared +library: true +dependencies: + codeql/ssa: ${workspace} + codeql/util: ${workspace} +warnOnImplicitThis: true diff --git a/shared/mad/CHANGELOG.md b/shared/mad/CHANGELOG.md index 5712c750565..ea8b30ac3fe 100644 --- a/shared/mad/CHANGELOG.md +++ b/shared/mad/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/mad/change-notes/released/0.1.2.md b/shared/mad/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/mad/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/mad/codeql-pack.release.yml b/shared/mad/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/mad/codeql-pack.release.yml +++ b/shared/mad/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml index a24493a6c10..09aa43faace 100644 --- a/shared/mad/qlpack.yml +++ b/shared/mad/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/mad -version: 0.1.1 +version: 0.1.2 groups: shared library: true dependencies: diff --git a/shared/regex/CHANGELOG.md b/shared/regex/CHANGELOG.md index 932a90a2b80..f81edeca8d2 100644 --- a/shared/regex/CHANGELOG.md +++ b/shared/regex/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/regex/change-notes/released/0.1.2.md b/shared/regex/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/regex/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/regex/codeql-pack.release.yml b/shared/regex/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/regex/codeql-pack.release.yml +++ b/shared/regex/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml index f83925a92b7..0e13edaa72f 100644 --- a/shared/regex/qlpack.yml +++ b/shared/regex/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/regex -version: 0.1.1 +version: 0.1.2 groups: shared library: true dependencies: diff --git a/shared/ssa/CHANGELOG.md b/shared/ssa/CHANGELOG.md index df49982f4bd..f4d3b9239cb 100644 --- a/shared/ssa/CHANGELOG.md +++ b/shared/ssa/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/ssa/change-notes/released/0.1.2.md b/shared/ssa/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/ssa/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/ssa/codeql-pack.release.yml b/shared/ssa/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/ssa/codeql-pack.release.yml +++ b/shared/ssa/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index c154e9a3cd2..b3fe99ea4d4 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ssa -version: 0.1.1 +version: 0.1.2 groups: shared library: true warnOnImplicitThis: true diff --git a/shared/tutorial/CHANGELOG.md b/shared/tutorial/CHANGELOG.md index 01dff93e6be..77ce73b6acc 100644 --- a/shared/tutorial/CHANGELOG.md +++ b/shared/tutorial/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/tutorial/change-notes/released/0.1.2.md b/shared/tutorial/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/tutorial/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/tutorial/codeql-pack.release.yml b/shared/tutorial/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/tutorial/codeql-pack.release.yml +++ b/shared/tutorial/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml index c8d13182ed6..6f28eac2fd6 100644 --- a/shared/tutorial/qlpack.yml +++ b/shared/tutorial/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/tutorial description: Library for the CodeQL detective tutorials, helping new users learn to write CodeQL queries. -version: 0.1.1 +version: 0.1.2 groups: shared library: true warnOnImplicitThis: true diff --git a/shared/typetracking/CHANGELOG.md b/shared/typetracking/CHANGELOG.md index 84420295d07..e5bed327a86 100644 --- a/shared/typetracking/CHANGELOG.md +++ b/shared/typetracking/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/typetracking/change-notes/released/0.1.2.md b/shared/typetracking/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/typetracking/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/typetracking/codeql-pack.release.yml b/shared/typetracking/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/typetracking/codeql-pack.release.yml +++ b/shared/typetracking/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml index 5af6571003f..a8b6c849241 100644 --- a/shared/typetracking/qlpack.yml +++ b/shared/typetracking/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typetracking -version: 0.1.1 +version: 0.1.2 groups: shared library: true dependencies: diff --git a/shared/typos/CHANGELOG.md b/shared/typos/CHANGELOG.md index da65658ea76..66d8aacf9d8 100644 --- a/shared/typos/CHANGELOG.md +++ b/shared/typos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/typos/change-notes/released/0.1.2.md b/shared/typos/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/typos/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/typos/codeql-pack.release.yml b/shared/typos/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/typos/codeql-pack.release.yml +++ b/shared/typos/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml index d9b78979125..2ffa8ea0a02 100644 --- a/shared/typos/qlpack.yml +++ b/shared/typos/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typos -version: 0.1.1 +version: 0.1.2 groups: shared library: true warnOnImplicitThis: true diff --git a/shared/util/CHANGELOG.md b/shared/util/CHANGELOG.md index e90bdd0f977..3faa7e80d47 100644 --- a/shared/util/CHANGELOG.md +++ b/shared/util/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 ### Deprecated APIs diff --git a/shared/util/change-notes/released/0.1.2.md b/shared/util/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/util/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/util/codeql-pack.release.yml b/shared/util/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/util/codeql-pack.release.yml +++ b/shared/util/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/util/codeql/util/Location.qll b/shared/util/codeql/util/Location.qll new file mode 100644 index 00000000000..8faa1ee4eeb --- /dev/null +++ b/shared/util/codeql/util/Location.qll @@ -0,0 +1,36 @@ +/** Provides classes for working with locations. */ + +/** + * A location as given by a file, a start line, a start column, + * an end line, and an end column. + * + * For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +signature class LocationSig { + /** Gets the 1-based line number (inclusive) where this location starts. */ + int getStartLine(); + + /** Gets the 1-based column number (inclusive) where this location starts. */ + int getStartColumn(); + + /** Gets the 1-based line number (inclusive) where this location ends. */ + int getEndLine(); + + /** Gets the 1-based column number (inclusive) where this location ends. */ + int getEndColumn(); + + /** Gets a textual representation of this location. */ + bindingset[this] + string toString(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startColumn` of line `startLine` to + * column `endColumn` of line `endLine` in file `filepath`. + * For more information, see + * [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filePath, int startLine, int startColumn, int endLine, int endColumn + ); +} diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml index 6f5546633d8..df2c69035f6 100644 --- a/shared/util/qlpack.yml +++ b/shared/util/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/util -version: 0.1.1 +version: 0.1.2 groups: shared library: true dependencies: diff --git a/shared/yaml/CHANGELOG.md b/shared/yaml/CHANGELOG.md index 84397a7f5ef..96f1fbefd02 100644 --- a/shared/yaml/CHANGELOG.md +++ b/shared/yaml/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +No user-facing changes. + ## 0.1.1 No user-facing changes. diff --git a/shared/yaml/change-notes/released/0.1.2.md b/shared/yaml/change-notes/released/0.1.2.md new file mode 100644 index 00000000000..9b0e2e7d717 --- /dev/null +++ b/shared/yaml/change-notes/released/0.1.2.md @@ -0,0 +1,3 @@ +## 0.1.2 + +No user-facing changes. diff --git a/shared/yaml/codeql-pack.release.yml b/shared/yaml/codeql-pack.release.yml index 92d1505475f..6abd14b1ef8 100644 --- a/shared/yaml/codeql-pack.release.yml +++ b/shared/yaml/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.1 +lastReleaseVersion: 0.1.2 diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml index 99029a70f3b..6a0bdf13d9c 100644 --- a/shared/yaml/qlpack.yml +++ b/shared/yaml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/yaml -version: 0.1.1 +version: 0.1.2 groups: shared library: true warnOnImplicitThis: true diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 77e27c00f41..24da98e1476 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -14,7 +14,6 @@ #include "swift/extractor/infra/file/Path.h" #include "swift/extractor/infra/SwiftLocationExtractor.h" #include "swift/extractor/infra/SwiftBodyEmissionStrategy.h" -#include "swift/extractor/mangler/SwiftMangler.h" #include "swift/logging/SwiftAssert.h" using namespace codeql; @@ -49,24 +48,20 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source } } -// TODO: This should be factored out/replaced with simplified version of custom mangling -static std::string mangledDeclName(const swift::ValueDecl& decl) { - std::string_view moduleName = decl.getModuleContext()->getRealName().str(); - // ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl` - if (decl.getKind() == swift::DeclKind::Module) { - return std::string{moduleName}; +static std::string mangledDeclName(const swift::Decl& decl) { + // SwiftRecursiveMangler mangled name cannot be used directly as it can be too long for file names + // so we build some minimal human readable info for debuggability and append a hash + auto ret = decl.getModuleContext()->getRealName().str().str(); + ret += '/'; + ret += swift::Decl::getKindName(decl.getKind()); + ret += '_'; + if (auto valueDecl = llvm::dyn_cast(&decl); valueDecl && valueDecl->hasName()) { + ret += valueDecl->getBaseName().userFacingName(); + ret += '_'; } - swift::Mangle::ASTMangler mangler; - if (decl.getKind() == swift::DeclKind::TypeAlias) { - // In cases like this (when coming from PCM) - // typealias CFXMLTree = CFTree - // typealias CFXMLTreeRef = CFXMLTree - // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' - // which is not correct and causes inconsistencies. mangleEntity makes these two distinct - // prefix adds a couple of special symbols, we don't necessary need them - return mangler.mangleEntity(&decl); - } - return mangler.mangleAnyDecl(&decl, /* prefix = */ false); + SwiftRecursiveMangler mangler; + ret += mangler.mangleDecl(decl).hash(); + return ret; } static fs::path getFilename(swift::ModuleDecl& module, @@ -76,20 +71,7 @@ static fs::path getFilename(swift::ModuleDecl& module, return resolvePath(primaryFile->getFilename()); } if (lazyDeclaration) { - // this code will be thrown away in the near future - auto decl = llvm::dyn_cast(lazyDeclaration); - CODEQL_ASSERT(decl, "not a ValueDecl"); - auto mangled = mangledDeclName(*decl); - // mangled name can be too long to use as a file name, so we can't use it directly - mangled = picosha2::hash256_hex_string(mangled); - std::string ret; - ret += module.getRealName().str(); - ret += '_'; - ret += decl->getBaseName().userFacingName(); - ret += '_'; - // half a SHA2 is enough - ret += std::string_view(mangled).substr(0, mangled.size() / 2); - return ret; + return mangledDeclName(*lazyDeclaration); } // PCM clang module if (module.isNonSwiftModule()) { diff --git a/swift/extractor/infra/BUILD.bazel b/swift/extractor/infra/BUILD.bazel index f2b7f6baee2..6a624844c76 100644 --- a/swift/extractor/infra/BUILD.bazel +++ b/swift/extractor/infra/BUILD.bazel @@ -11,5 +11,6 @@ swift_cc_library( "//swift/extractor/trap", "//swift/logging", "//swift/third_party/swift-llvm-support", + "@picosha2", ], ) diff --git a/swift/extractor/infra/SwiftLocationExtractor.cpp b/swift/extractor/infra/SwiftLocationExtractor.cpp index de7c5aff70d..fd9ae9487b9 100644 --- a/swift/extractor/infra/SwiftLocationExtractor.cpp +++ b/swift/extractor/infra/SwiftLocationExtractor.cpp @@ -25,8 +25,8 @@ void SwiftLocationExtractor::attachLocationImpl(const swift::SourceManager& sour entry.file = fetchFileLabel(file); std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start); std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end); - SwiftMangledName locName{{"loc", entry.file, ":", entry.start_line, ":", entry.start_column, ":", - entry.end_line, ":", entry.end_column}}; + SwiftMangledName locName{"loc", entry.file, ':', entry.start_line, ':', entry.start_column, + ':', entry.end_line, ':', entry.end_column}; entry.id = trap.createTypedLabel(locName); trap.emit(entry); trap.emit(LocatableLocationsTrap{locatableLabel, entry.id}); @@ -99,7 +99,7 @@ TrapLabel SwiftLocationExtractor::fetchFileLabel(const std::filesystem: } DbFile entry({}); - entry.id = trap.createTypedLabel({{"file_", file.string()}}); + entry.id = trap.createTypedLabel({"file_", file.string()}); entry.name = file.string(); trap.emit(entry); store[file] = entry.id; diff --git a/swift/extractor/infra/SwiftMangledName.cpp b/swift/extractor/infra/SwiftMangledName.cpp index d4c88c3d314..b951cc23f69 100644 --- a/swift/extractor/infra/SwiftMangledName.cpp +++ b/swift/extractor/infra/SwiftMangledName.cpp @@ -1,29 +1,25 @@ #include "swift/extractor/infra/SwiftMangledName.h" #include "absl/strings/str_cat.h" +#include + namespace codeql { -namespace { -void appendPart(std::string& out, const std::string& prefix) { - out += prefix; +std::string SwiftMangledName::hash() const { + auto ret = picosha2::hash256_hex_string(value); + // half a hash is already enough for disambuiguation + ret.resize(ret.size() / 2); + return ret; } -void appendPart(std::string& out, UntypedTrapLabel label) { - absl::StrAppend(&out, "{", label.str(), "}"); +SwiftMangledName& SwiftMangledName::operator<<(UntypedTrapLabel label) & { + absl::StrAppend(&value, "{", label.str(), "}"); + return *this; } -void appendPart(std::string& out, unsigned index) { - absl::StrAppend(&out, index); -} - -} // namespace - -std::string SwiftMangledName::str() const { - std::string out; - for (const auto& part : parts) { - std::visit([&](const auto& contents) { appendPart(out, contents); }, part); - } - return out; +SwiftMangledName& SwiftMangledName::operator<<(unsigned int i) & { + absl::StrAppend(&value, i); + return *this; } } // namespace codeql diff --git a/swift/extractor/infra/SwiftMangledName.h b/swift/extractor/infra/SwiftMangledName.h index 8eaa0070593..24741f2ee24 100644 --- a/swift/extractor/infra/SwiftMangledName.h +++ b/swift/extractor/infra/SwiftMangledName.h @@ -9,37 +9,52 @@ namespace codeql { -struct SwiftMangledName { +class SwiftMangledName { + public: using Part = std::variant; - std::vector parts; + explicit operator bool() const { return !value.empty(); } - explicit operator bool() const { return !parts.empty(); } + const std::string& str() const { return value; } + operator std::string_view() const { return value; } - std::string str() const; + std::string hash() const; - // streaming labels or ints into a SwiftMangledName just appends them - template - SwiftMangledName& operator<<(TrapLabel label) { - parts.emplace_back(label); - return *this; + // let's avoid copying as long as we don't need it + SwiftMangledName() = default; + SwiftMangledName(const SwiftMangledName&) = delete; + SwiftMangledName& operator=(const SwiftMangledName&) = delete; + SwiftMangledName(SwiftMangledName&&) = default; + SwiftMangledName& operator=(SwiftMangledName&&) = default; + + template + SwiftMangledName(Args&&... args) { + (operator<<(std::forward(args)), ...); } - SwiftMangledName& operator<<(unsigned i) { - parts.emplace_back(i); - return *this; + SwiftMangledName& operator<<(UntypedTrapLabel label) &; + SwiftMangledName& operator<<(unsigned i) &; + + template + SwiftMangledName& operator<<(TrapLabel label) & { + return operator<<(static_cast(label)); } // streaming string-like stuff will add a new part it only if strictly required, otherwise it will // append to the last part if it is a string template - SwiftMangledName& operator<<(T&& arg) { - if (parts.empty() || !std::holds_alternative(parts.back())) { - parts.emplace_back(""); - } - std::get(parts.back()) += std::forward(arg); + SwiftMangledName& operator<<(T&& arg) & { + value += arg; return *this; } + + template + SwiftMangledName&& operator<<(T&& arg) && { + return std::move(operator<<(std::forward(arg))); + } + + private: + std::string value; }; } // namespace codeql diff --git a/swift/extractor/mangler/SwiftMangler.cpp b/swift/extractor/mangler/SwiftMangler.cpp index 2614652df68..409367eafd1 100644 --- a/swift/extractor/mangler/SwiftMangler.cpp +++ b/swift/extractor/mangler/SwiftMangler.cpp @@ -1,89 +1,403 @@ #include "swift/extractor/mangler/SwiftMangler.h" #include "swift/extractor/infra/SwiftDispatcher.h" #include "swift/extractor/trap/generated/decl/TrapClasses.h" +#include "swift/logging/SwiftLogging.h" + #include +#include +#include +#include #include using namespace codeql; namespace { -SwiftMangledName initMangled(const swift::TypeBase* type) { +Logger& logger() { + static Logger ret{"mangler"}; + return ret; +} + +const swift::Decl* getParent(const swift::Decl* decl) { + auto context = decl->getDeclContext(); + if (context->getContextKind() == swift::DeclContextKind::FileUnit) { + return decl->getModuleContext(); + } + return context->getAsDecl(); +} + +std::string_view getTypeKindStr(const swift::TypeBase* type) { switch (type->getKind()) { #define TYPE(ID, PARENT) \ case swift::TypeKind::ID: \ - return {{#ID "Type_"}}; + return #ID "Type"; #include default: return {}; } } -SwiftMangledName initMangled(const swift::Decl* decl) { - SwiftMangledName ret; - ret << swift::Decl::getKindName(decl->getKind()) << "Decl_"; - return ret; -} } // namespace +SwiftMangledName SwiftMangler::initMangled(const swift::TypeBase* type) { + return {getTypeKindStr(type), '_'}; +} + +SwiftMangledName SwiftMangler::initMangled(const swift::Decl* decl) { + return {swift::Decl::getKindName(decl->getKind()), "Decl_", fetch(getParent(decl))}; +} + SwiftMangledName SwiftMangler::mangleModuleName(std::string_view name) { - SwiftMangledName ret = {{"ModuleDecl_"}}; - ret << name; - return ret; + return {"ModuleDecl_", name}; } -SwiftMangledName SwiftMangler::mangleDecl(const swift::Decl& decl) { - if (!llvm::isa(decl)) { - return {}; - } - // We do not deduplicate local variables, but for the moment also non-local vars from non-swift - // (PCM, clang modules) modules as the mangler crashes sometimes - if (decl.getKind() == swift::DeclKind::Var && - (decl.getDeclContext()->isLocalContext() || decl.getModuleContext()->isNonSwiftModule())) { - return {}; - } - - // we do not deduplicate GenericTypeParamDecl, as their mangling is ambiguous in the presence of - // extensions - if (decl.getKind() == swift::DeclKind::GenericTypeParam) { - return {}; - } - - if (decl.getKind() == swift::DeclKind::Module) { - return mangleModuleName(llvm::cast(decl).getRealName().str()); - } - - auto ret = initMangled(&decl); - const auto& valueDecl = llvm::cast(decl); - // stamp all declarations with an id-ref of the containing module - auto moduleLabel = dispatcher.fetchLabel(decl.getModuleContext()); - ret << moduleLabel; - if (decl.getKind() == swift::DeclKind::TypeAlias) { - // In cases like this (when coming from PCM) - // typealias CFXMLTree = CFTree - // typealias CFXMLTreeRef = CFXMLTree - // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' - // which is not correct and causes inconsistencies. mangleEntity makes these two distinct - // prefix adds a couple of special symbols, we don't necessary need them - ret << mangler.mangleEntity(&valueDecl); - } else { - // prefix adds a couple of special symbols, we don't necessary need them - ret << mangler.mangleAnyDecl(&valueDecl, /* prefix = */ false); - } - return ret; -} - -SwiftMangledName SwiftMangler::visitModuleType(const swift::ModuleType* type) { - auto ret = initMangled(type); - ret << type->getModule()->getRealName().str(); - if (type->getModule()->isNonSwiftModule()) { +SwiftMangledName SwiftMangler::visitModuleDecl(const swift::ModuleDecl* decl) { + auto ret = mangleModuleName(decl->getRealName().str()); + if (decl->isNonSwiftModule()) { ret << "|clang"; } return ret; } -SwiftMangledName SwiftMangler::visitBuiltinType(const swift::BuiltinType* type) { - auto ret = initMangled(type); - llvm::SmallString<32> buffer; - ret << type->getTypeName(buffer, /* prependBuiltinNamespace= */ false); +SwiftMangledName SwiftMangler::visitValueDecl(const swift::ValueDecl* decl, bool force) { + if (!force && (!decl->hasName() || decl->getDeclContext()->isLocalContext())) { + return {}; + } + auto ret = initMangled(decl); + std::string name; + llvm::raw_string_ostream oss{name}; + decl->getName().print(oss); + ret << name; + if (decl->isStatic()) { + ret << "|static"; + } return ret; } + +SwiftMangledName SwiftMangler::visitTypeDiscriminatedValueDecl(const swift::ValueDecl* decl) { + if (auto ret = visitValueDecl(decl)) { + ret << fetch(decl->getInterfaceType()->getCanonicalType()); + return ret; + } + return {}; +} + +SwiftMangledName SwiftMangler::visitAbstractFunctionDecl(const swift::AbstractFunctionDecl* decl) { + return visitTypeDiscriminatedValueDecl(decl); +} + +SwiftMangledName SwiftMangler::visitSubscriptDecl(const swift::SubscriptDecl* decl) { + return visitTypeDiscriminatedValueDecl(decl); +} + +SwiftMangledName SwiftMangler::visitVarDecl(const swift::VarDecl* decl) { + return visitTypeDiscriminatedValueDecl(decl); +} + +SwiftMangledName SwiftMangler::visitExtensionDecl(const swift::ExtensionDecl* decl) { + if (decl->getDeclContext()->isLocalContext()) { + return {}; + } + + auto parent = getParent(decl); + return initMangled(decl) << fetch(parent) << getExtensionIndex(decl, parent); +} + +unsigned SwiftMangler::getExtensionIndex(const swift::ExtensionDecl* decl, + const swift::Decl* parent) { + // to avoid iterating multiple times on the parent of multiple extensions, we preload extension + // indexes once for each encountered parent into the `preloadedExtensionIndexes` mapping. + // Because we mangle declarations only once in a given trap/dispatcher context, we can safely + // discard preloaded indexes on use + if (auto found = preloadedExtensionIndexes.extract(decl)) { + return found.mapped(); + } + if (auto parentModule = llvm::dyn_cast(parent)) { + llvm::SmallVector siblings; + parentModule->getTopLevelDecls(siblings); + indexExtensions(siblings); + } else if (auto iterableParent = llvm::dyn_cast(parent)) { + indexExtensions(iterableParent->getAllMembers()); + } else { + // TODO use a generic logging handle for Swift entities here, once it's available + CODEQL_ASSERT(false, "non-local context must be module or iterable decl context"); + } + auto found = preloadedExtensionIndexes.extract(decl); + // TODO use a generic logging handle for Swift entities here, once it's available + CODEQL_ASSERT(found, "extension not found within parent"); + return found.mapped(); +} + +void SwiftMangler::indexExtensions(llvm::ArrayRef siblings) { + auto index = 0u; + for (auto sibling : siblings) { + if (sibling->getKind() == swift::DeclKind::Extension) { + preloadedExtensionIndexes.emplace(sibling, index); + } + ++index; + } +} + +SwiftMangledName SwiftMangler::visitGenericTypeParamDecl(const swift::GenericTypeParamDecl* decl) { + return visitValueDecl(decl, /*force=*/true) << '_' << decl->getDepth() << '_' << decl->getIndex(); +} + +SwiftMangledName SwiftMangler::visitAssociatedTypeDecl(const swift::AssociatedTypeDecl* decl) { + return visitValueDecl(decl, /*force=*/true); +} + +SwiftMangledName SwiftMangler::visitModuleType(const swift::ModuleType* type) { + return initMangled(type) << fetch(type->getModule()); +} + +SwiftMangledName SwiftMangler::visitTupleType(const swift::TupleType* type) { + auto ret = initMangled(type); + for (const auto& element : type->getElements()) { + if (element.hasName()) { + ret << element.getName().str(); + } + ret << fetch(element.getType()); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitBuiltinType(const swift::BuiltinType* type) { + llvm::SmallString<32> buffer; + return initMangled(type) << type->getTypeName(buffer, /* prependBuiltinNamespace= */ false); +} + +SwiftMangledName SwiftMangler::visitAnyGenericType(const swift::AnyGenericType* type) { + auto ret = initMangled(type); + auto decl = type->getDecl(); + ret << fetch(decl); + if (auto parent = type->getParent()) { + ret << fetch(parent); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitType(const swift::TypeBase* type) { + LOG_WARNING("encountered {} for which we give no name", getTypeKindStr(type)); + return {}; +} + +SwiftMangledName SwiftMangler::visitBoundGenericType(const swift::BoundGenericType* type) { + auto ret = visitAnyGenericType(type); + for (const auto param : type->getGenericArgs()) { + ret << fetch(param); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitAnyFunctionType(const swift::AnyFunctionType* type) { + auto ret = initMangled(type); + for (const auto& param : type->getParams()) { + ret << fetch(param.getPlainType()); + if (param.isInOut()) { + ret << "_inout"; + } + if (param.isOwned()) { + ret << "_owned"; + } + if (param.isShared()) { + ret << "_shared"; + } + if (param.isIsolated()) { + ret << "_isolated"; + } + if (param.isVariadic()) { + ret << "..."; + } + } + ret << "->" << fetch(type->getResult()); + if (type->isAsync()) { + ret << "_async"; + } + if (type->isThrowing()) { + ret << "_throws"; + } + if (type->isSendable()) { + ret << "_sendable"; + } + if (type->isNoEscape()) { + ret << "_noescape"; + } + if (type->hasGlobalActor()) { + ret << "_actor" << fetch(type->getGlobalActor()); + } + // TODO: see if this needs to be used in identifying types, if not it needs to be removed from + // type printing in the Swift compiler code + assert(type->hasExtInfo() && "type must have ext info"); + auto info = type->getExtInfo(); + auto convention = info.getSILRepresentation(); + if (convention != swift::SILFunctionTypeRepresentation::Thick) { + ret << "_convention" << static_cast(convention); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitGenericFunctionType(const swift::GenericFunctionType* type) { + auto ret = visitAnyFunctionType(type); + ret << '<'; + for (auto paramType : type->getGenericParams()) { + ret << fetch(paramType); + } + ret << '>'; + if (!type->getRequirements().empty()) { + ret << "where_"; + for (const auto& req : type->getRequirements()) { + ret << fetch(req.getFirstType().getPointer()); + ret << (req.getKind() == swift::RequirementKind::SameType ? '=' : ':'); + if (req.getKind() == swift::RequirementKind::Layout) { + ret << '(' << req.getLayoutConstraint().getString() << ')'; + } else { + ret << fetch(req.getSecondType()); + } + } + } + return ret; +} + +SwiftMangledName SwiftMangler::visitGenericTypeParamType(const swift::GenericTypeParamType* type) { + auto ret = initMangled(type); + if (auto decl = type->getDecl()) { + ret << fetch(decl); + } else { + // type parameter is canonicalized to a depth/index coordinate + ret << type->getDepth() << '_' << type->getIndex(); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitAnyMetatypeType(const swift::AnyMetatypeType* type) { + return initMangled(type) << fetch(type->getInstanceType()); +} + +SwiftMangledName SwiftMangler::visitDependentMemberType(const swift::DependentMemberType* type) { + return initMangled(type) << fetch(type->getBase()) << fetch(type->getAssocType()); +} + +SwiftMangledName SwiftMangler::visitInOutType(const swift::InOutType* type) { + return initMangled(type) << fetch(type->getObjectType()); +} + +SwiftMangledName SwiftMangler::visitExistentialType(const swift::ExistentialType* type) { + return initMangled(type) << fetch(type->getConstraintType()); +} + +SwiftMangledName SwiftMangler::visitUnarySyntaxSugarType(const swift::UnarySyntaxSugarType* type) { + return initMangled(type) << fetch(type->getBaseType()); +} + +SwiftMangledName SwiftMangler::visitDictionaryType(const swift::DictionaryType* type) { + return initMangled(type) << fetch(type->getKeyType()) << fetch(type->getValueType()); +} + +SwiftMangledName SwiftMangler::visitTypeAliasType(const swift::TypeAliasType* type) { + auto ret = initMangled(type); + ret << fetch(type->getDecl()); + if (auto parent = type->getParent()) { + ret << fetch(parent); + } + ret << '<'; + for (auto replacement : type->getSubstitutionMap().getReplacementTypes()) { + ret << fetch(replacement); + } + ret << '>'; + return ret; +} + +SwiftMangledName SwiftMangler::visitArchetypeType(const swift::ArchetypeType* type) { + auto ret = initMangled(type) << fetch(type->getInterfaceType()); + if (const auto super = type->getSuperclass()) { + ret << ':' << fetch(super); + } + for (const auto* protocol : type->getConformsTo()) { + // Including the protocols in the mangled name allows us to distinguish the "same" type in + // different extensions, where it might have different constraints. Mangling the context (i.e. + // which ExtensionDecl or ValueDecl it's mentioned in) might be more robust, but there doesn't + // seem to be a clean way to get it. + ret << ':' << fetch(protocol); + } + return ret; +} + +SwiftMangledName SwiftMangler::visitOpaqueTypeArchetypeType( + const swift::OpaqueTypeArchetypeType* type) { + return visitArchetypeType(type) << fetch(type->getDecl()); +} + +SwiftMangledName SwiftMangler::visitOpenedArchetypeType(const swift::OpenedArchetypeType* type) { + llvm::SmallVector uuid; + type->getOpenedExistentialID().toString(uuid); + return visitArchetypeType(type) << std::string_view(uuid.data(), uuid.size()); +} + +SwiftMangledName SwiftMangler::visitProtocolCompositionType( + const swift::ProtocolCompositionType* type) { + auto ret = initMangled(type); + for (auto composed : type->getMembers()) { + ret << fetch(composed); + } + if (type->hasExplicitAnyObject()) { + ret << "&AnyObject"; + } + return ret; +} + +SwiftMangledName SwiftMangler::visitParenType(const swift::ParenType* type) { + return initMangled(type) << fetch(type->getUnderlyingType()); +} + +SwiftMangledName SwiftMangler::visitLValueType(const swift::LValueType* type) { + return initMangled(type) << fetch(type->getObjectType()); +} + +SwiftMangledName SwiftMangler::visitDynamicSelfType(const swift::DynamicSelfType* type) { + return initMangled(type) << fetch(type->getSelfType()); +} + +SwiftMangledName SwiftMangler::visitUnboundGenericType(const swift::UnboundGenericType* type) { + return initMangled(type) << fetch(type->getDecl()); +} + +SwiftMangledName SwiftMangler::visitReferenceStorageType(const swift::ReferenceStorageType* type) { + return initMangled(type) << fetch(type->getReferentType()); +} + +SwiftMangledName SwiftMangler::visitParametrizedProtocolType( + const swift::ParameterizedProtocolType* type) { + auto ret = initMangled(type); + ret << fetch(type->getBaseType()); + ret << '<'; + for (auto arg : type->getArgs()) { + ret << fetch(arg); + } + ret << '>'; + return ret; +} + +namespace { +template +UntypedTrapLabel fetchLabel(SwiftDispatcher& dispatcher, const E* e) { + auto ret = dispatcher.fetchLabel(e); + // TODO use a generic logging handle for Swift entities here, once it's available + CODEQL_ASSERT(ret.valid(), "using an undefined label in mangling"); + return ret; +} +} // namespace + +SwiftMangledName SwiftTrapMangler::fetch(const swift::Decl* decl) { + return {fetchLabel(dispatcher, decl)}; +} + +SwiftMangledName SwiftTrapMangler::fetch(const swift::TypeBase* type) { + return {fetchLabel(dispatcher, type)}; +} + +SwiftMangledName SwiftRecursiveMangler::fetch(const swift::Decl* decl) { + return mangleDecl(*decl).hash(); +} + +SwiftMangledName SwiftRecursiveMangler::fetch(const swift::TypeBase* type) { + return mangleType(*type).hash(); +} diff --git a/swift/extractor/mangler/SwiftMangler.h b/swift/extractor/mangler/SwiftMangler.h index a8e975d0094..d0597448e83 100644 --- a/swift/extractor/mangler/SwiftMangler.h +++ b/swift/extractor/mangler/SwiftMangler.h @@ -11,39 +11,132 @@ #include "swift/extractor/infra/SwiftMangledName.h" #include +#include namespace codeql { class SwiftDispatcher; -class SwiftMangler : private swift::TypeVisitor { +// This class is tasked with assigning unique names to entities that need it (non-local +// declarations and types), to be used as trap keys. +// This uses the Template Method design pattern (or Non-Virtual Interface). The actual behavior +// when the identity depends on some other entity (like the parent of a declaration, or the +// declaration introducing a user type) depends on a private virtual fetch method. See below for +// the specific implementations. +// +// * all names are prefixed with the name of the entity class (for example `ParamDecl_`) +// * declarations usually use a fetch of their declaration context as first element, followed +// by whatever distinguishes them within that context (the name, or the signature for function) +// * user defined types have a name that is a simple wrapper around a fetch of their declaration +class SwiftMangler : private swift::TypeVisitor, + private swift::DeclVisitor { + using TypeVisitor = swift::TypeVisitor; + using DeclVisitor = swift::DeclVisitor; + public: - explicit SwiftMangler(SwiftDispatcher& dispatcher) : dispatcher(dispatcher) {} + virtual ~SwiftMangler() = default; static SwiftMangledName mangleModuleName(std::string_view name); - // TODO actual visit - SwiftMangledName mangleDecl(const swift::Decl& decl); + SwiftMangledName mangleDecl(const swift::Decl& decl) { + return DeclVisitor::visit(const_cast(&decl)); + } SwiftMangledName mangleType(const swift::TypeBase& type) { - return visit(const_cast(&type)); + return TypeVisitor::visit(const_cast(&type)); } private: - friend class swift::TypeVisitor; + friend TypeVisitor; + friend DeclVisitor; - // default fallback for not yet mangled types. This should never be called in normal situations - // will just spawn a random name - // TODO: make it assert once we mangle all types - static SwiftMangledName visitType(const swift::TypeBase* type) { return {}; } + // assign no name by default + static SwiftMangledName visitDecl(const swift::Decl* decl) { return {}; } + + // current default, falling back to internal mangling + SwiftMangledName visitValueDecl(const swift::ValueDecl* decl, bool force = false); + + SwiftMangledName visitModuleDecl(const swift::ModuleDecl* decl); + SwiftMangledName visitExtensionDecl(const swift::ExtensionDecl* decl); + SwiftMangledName visitAbstractFunctionDecl(const swift::AbstractFunctionDecl* decl); + SwiftMangledName visitSubscriptDecl(const swift::SubscriptDecl* decl); + SwiftMangledName visitVarDecl(const swift::VarDecl* decl); + SwiftMangledName visitGenericTypeParamDecl(const swift::GenericTypeParamDecl* decl); + SwiftMangledName visitAssociatedTypeDecl(const swift::AssociatedTypeDecl* decl); + + // default fallback for non mangled types. This covers types that should not appear in normal + // successful extractor runs, like ErrorType + SwiftMangledName visitType(const swift::TypeBase* type); SwiftMangledName visitModuleType(const swift::ModuleType* type); - + SwiftMangledName visitTupleType(const swift::TupleType* type); SwiftMangledName visitBuiltinType(const swift::BuiltinType* type); + SwiftMangledName visitAnyGenericType(const swift::AnyGenericType* type); + + // shouldn't be required, but they forgot to link `NominalType` to its direct superclass + // in swift/AST/TypeNodes.def, so we need to chain the call manually + SwiftMangledName visitNominalType(const swift::NominalType* type) { + return visitAnyGenericType(type); + } + + SwiftMangledName visitBoundGenericType(const swift::BoundGenericType* type); + SwiftMangledName visitAnyFunctionType(const swift::AnyFunctionType* type); + SwiftMangledName visitGenericFunctionType(const swift::GenericFunctionType* type); + SwiftMangledName visitGenericTypeParamType(const swift::GenericTypeParamType* type); + SwiftMangledName visitAnyMetatypeType(const swift::AnyMetatypeType* type); + SwiftMangledName visitDependentMemberType(const swift::DependentMemberType* type); + SwiftMangledName visitInOutType(const swift::InOutType* type); + SwiftMangledName visitExistentialType(const swift::ExistentialType* type); + SwiftMangledName visitUnarySyntaxSugarType(const swift::UnarySyntaxSugarType* type); + SwiftMangledName visitDictionaryType(const swift::DictionaryType* type); + SwiftMangledName visitTypeAliasType(const swift::TypeAliasType* type); + SwiftMangledName visitArchetypeType(const swift::ArchetypeType* type); + SwiftMangledName visitOpaqueTypeArchetypeType(const swift::OpaqueTypeArchetypeType* type); + SwiftMangledName visitOpenedArchetypeType(const swift::OpenedArchetypeType* type); + SwiftMangledName visitProtocolCompositionType(const swift::ProtocolCompositionType* type); + SwiftMangledName visitParenType(const swift::ParenType* type); + SwiftMangledName visitLValueType(const swift::LValueType* type); + SwiftMangledName visitDynamicSelfType(const swift::DynamicSelfType* type); + SwiftMangledName visitUnboundGenericType(const swift::UnboundGenericType* type); + SwiftMangledName visitReferenceStorageType(const swift::ReferenceStorageType* type); + SwiftMangledName visitParametrizedProtocolType(const swift::ParameterizedProtocolType* type); private: - swift::Mangle::ASTMangler mangler; + std::unordered_map preloadedExtensionIndexes; + + virtual SwiftMangledName fetch(const swift::Decl* decl) = 0; + virtual SwiftMangledName fetch(const swift::TypeBase* type) = 0; + SwiftMangledName fetch(swift::Type type) { return fetch(type.getPointer()); } + + void indexExtensions(llvm::ArrayRef siblings); + unsigned int getExtensionIndex(const swift::ExtensionDecl* decl, const swift::Decl* parent); + static SwiftMangledName initMangled(const swift::TypeBase* type); + SwiftMangledName initMangled(const swift::Decl* decl); + SwiftMangledName visitTypeDiscriminatedValueDecl(const swift::ValueDecl* decl); +}; + +// This implementation is indented for use in defining trap keys. In this case fetching gives +// a [trap id-ref][1] is used, using the dispatcher to give us a label reference to that entity. +// Because that entity will also generally have a mangled name, it is important that this does not +// lead to any recursive loop (which is checked at runtime within the dispatcher). +// +// [1]: https://github.com/github/codeql-core/blob/main/wiki/extractors/trap.md#ids +class SwiftTrapMangler : public SwiftMangler { + public: + explicit SwiftTrapMangler(SwiftDispatcher& dispatcher) : dispatcher(dispatcher) {} + + private: + SwiftMangledName fetch(const swift::Decl* decl) override; + SwiftMangledName fetch(const swift::TypeBase* type) override; + SwiftDispatcher& dispatcher; }; +// In this implementation, fetching gives a hash of the mangled name itself, leading to a direct +// recursion. This is intended for use in trap file names. +class SwiftRecursiveMangler : public SwiftMangler { + SwiftMangledName fetch(const swift::Decl* decl) override; + SwiftMangledName fetch(const swift::TypeBase* type) override; +}; + } // namespace codeql diff --git a/swift/extractor/translators/PatternTranslator.cpp b/swift/extractor/translators/PatternTranslator.cpp index ee119af1c30..14a913d26b4 100644 --- a/swift/extractor/translators/PatternTranslator.cpp +++ b/swift/extractor/translators/PatternTranslator.cpp @@ -59,7 +59,11 @@ codeql::IsPattern PatternTranslator::translateIsPattern(const swift::IsPattern& codeql::ExprPattern PatternTranslator::translateExprPattern(const swift::ExprPattern& pattern) { auto entry = dispatcher.createEntry(pattern); - entry.sub_expr = dispatcher.fetchLabel(pattern.getSubExpr()); + if (auto match = pattern.getMatchExpr()) { + entry.sub_expr = dispatcher.fetchLabel(match); + } else { + entry.sub_expr = dispatcher.fetchLabel(pattern.getSubExpr()); + } return entry; } diff --git a/swift/extractor/translators/SwiftVisitor.h b/swift/extractor/translators/SwiftVisitor.h index db38d9608ee..b6e5ee0875e 100644 --- a/swift/extractor/translators/SwiftVisitor.h +++ b/swift/extractor/translators/SwiftVisitor.h @@ -58,7 +58,7 @@ class SwiftVisitor : private SwiftDispatcher { StmtTranslator stmtTranslator{*this}; TypeTranslator typeTranslator{*this}; PatternTranslator patternTranslator{*this}; - SwiftMangler mangler{*this}; + SwiftTrapMangler mangler{*this}; }; } // namespace codeql diff --git a/swift/extractor/trap/TrapLabel.h b/swift/extractor/trap/TrapLabel.h index e8b374faff0..629aa3e0033 100644 --- a/swift/extractor/trap/TrapLabel.h +++ b/swift/extractor/trap/TrapLabel.h @@ -40,6 +40,7 @@ class UntypedTrapLabel { } std::string str() const { + assert(valid() && "outputting an undefined label!"); std::string ret(strSize(), '\0'); ret[0] = '#'; std::to_chars(ret.data() + 1, ret.data() + ret.size(), id_, 16); diff --git a/swift/integration-tests/posix-only/deduplication/Decls.expected b/swift/integration-tests/posix-only/deduplication/Decls.expected index 5afdb81804a..edfdbafdbf5 100644 --- a/swift/integration-tests/posix-only/deduplication/Decls.expected +++ b/swift/integration-tests/posix-only/deduplication/Decls.expected @@ -1,16 +1,93 @@ -| Sources/deduplication/def.swift:1:1:1:9 | var ... = ... | PatternBindingDecl | -| Sources/deduplication/def.swift:1:5:1:5 | x | ConcreteVarDecl | -| Sources/deduplication/def.swift:3:1:3:20 | Generic | StructDecl | -| Sources/deduplication/def.swift:3:8:3:8 | Generic.init() | Initializer | -| Sources/deduplication/def.swift:3:8:3:8 | self | ParamDecl | -| Sources/deduplication/def.swift:3:16:3:16 | T | GenericTypeParamDecl | -| Sources/deduplication/def.swift:5:1:5:41 | var ... = ... | PatternBindingDecl | -| Sources/deduplication/def.swift:5:5:5:5 | instantiated_generic | ConcreteVarDecl | -| Sources/deduplication/def.swift:7:1:7:42 | function(_:) | NamedFunction | -| Sources/deduplication/def.swift:7:15:7:18 | _ | ParamDecl | -| Sources/deduplication/use.swift:1:1:1:13 | var ... = ... | PatternBindingDecl | -| Sources/deduplication/use.swift:1:5:1:5 | use_x | ConcreteVarDecl | -| Sources/deduplication/use.swift:2:1:2:32 | var ... = ... | PatternBindingDecl | -| Sources/deduplication/use.swift:2:5:2:5 | use_instantiated_generic | ConcreteVarDecl | -| Sources/deduplication/use.swift:3:1:3:20 | var ... = ... | PatternBindingDecl | -| Sources/deduplication/use.swift:3:5:3:5 | use_function | ConcreteVarDecl | +| Sources/deduplication/def.swift:1:1:1:15 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/def.swift:1:5:1:5 | def_int | ConcreteVarDecl | Int [StructType] | +| Sources/deduplication/def.swift:3:1:5:1 | Generic | StructDecl | Generic.Type [MetatypeType] | +| Sources/deduplication/def.swift:3:8:3:8 | Generic.init() | Initializer | (Generic.Type) -> () -> Generic [GenericFunctionType] | +| Sources/deduplication/def.swift:3:8:3:8 | self | ParamDecl | Generic [BoundGenericStructType] | +| Sources/deduplication/def.swift:3:16:3:16 | T | GenericTypeParamDecl | T.Type [MetatypeType] | +| Sources/deduplication/def.swift:4:5:4:70 | def_generic_method(t:u:v:) | NamedFunction | (Generic) -> (T, U, V) -> T [GenericFunctionType] | +| Sources/deduplication/def.swift:4:10:4:10 | self | ParamDecl | Generic [BoundGenericStructType] | +| Sources/deduplication/def.swift:4:29:4:29 | U | GenericTypeParamDecl | U.Type [MetatypeType] | +| Sources/deduplication/def.swift:4:32:4:32 | V | GenericTypeParamDecl | V.Type [MetatypeType] | +| Sources/deduplication/def.swift:4:35:4:38 | t | ParamDecl | T [GenericTypeParamType] | +| Sources/deduplication/def.swift:4:41:4:44 | u | ParamDecl | U [GenericTypeParamType] | +| Sources/deduplication/def.swift:4:47:4:50 | v | ParamDecl | V [GenericTypeParamType] | +| Sources/deduplication/def.swift:7:1:7:45 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/def.swift:7:5:7:5 | def_instantiated_generic | ConcreteVarDecl | Generic [BoundGenericStructType] | +| Sources/deduplication/def.swift:9:1:9:46 | def_function(_:) | NamedFunction | (Int) -> Int [FunctionType] | +| Sources/deduplication/def.swift:9:19:9:22 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:11:1:11:77 | def_function_overloaded_on_return(_:_:) | NamedFunction | (Int, Double) -> Int [FunctionType] | +| Sources/deduplication/def.swift:11:40:11:43 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:11:48:11:51 | _ | ParamDecl | Double [StructType] | +| Sources/deduplication/def.swift:12:1:12:82 | def_function_overloaded_on_return(_:_:) | NamedFunction | (Int, Double) -> Double [FunctionType] | +| Sources/deduplication/def.swift:12:40:12:43 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:12:48:12:51 | _ | ParamDecl | Double [StructType] | +| Sources/deduplication/def.swift:14:1:14:57 | def_function_overloaded_on_parameter_type(_:) | NamedFunction | (Int) -> () [FunctionType] | +| Sources/deduplication/def.swift:14:48:14:51 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:15:1:15:60 | def_function_overloaded_on_parameter_type(_:) | NamedFunction | (Double) -> () [FunctionType] | +| Sources/deduplication/def.swift:15:48:15:51 | _ | ParamDecl | Double [StructType] | +| Sources/deduplication/def.swift:17:1:17:62 | def_function_overloaded_on_parameter_label(one:) | NamedFunction | (Int) -> () [FunctionType] | +| Sources/deduplication/def.swift:17:49:17:56 | x | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:18:1:18:62 | def_function_overloaded_on_parameter_label(two:) | NamedFunction | (Int) -> () [FunctionType] | +| Sources/deduplication/def.swift:18:49:18:56 | x | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:20:1:20:44 | def_throwing_function(_:) | NamedFunction | (Int) throws -> () [FunctionType] | +| Sources/deduplication/def.swift:20:28:20:31 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:21:1:21:60 | def_rethrowing_function(_:) | NamedFunction | (() throws -> ()) throws -> () [FunctionType] | +| Sources/deduplication/def.swift:21:30:21:47 | _ | ParamDecl | () throws -> () [FunctionType] | +| Sources/deduplication/def.swift:22:1:22:40 | def_async_function(_:) | NamedFunction | (Int) async -> () [FunctionType] | +| Sources/deduplication/def.swift:22:25:22:28 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:23:1:23:56 | def_async_throwing_function(_:) | NamedFunction | (Int) async throws -> () [FunctionType] | +| Sources/deduplication/def.swift:23:34:23:37 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/def.swift:24:1:24:72 | def_async_rethrowing_function(_:) | NamedFunction | (() throws -> ()) async throws -> () [FunctionType] | +| Sources/deduplication/def.swift:24:36:24:53 | _ | ParamDecl | () throws -> () [FunctionType] | +| Sources/deduplication/def.swift:26:1:26:55 | def_generic_function(_:_:_:) | NamedFunction | (A, B, C) -> () [GenericFunctionType] | +| Sources/deduplication/def.swift:26:27:26:27 | A | GenericTypeParamDecl | A.Type [MetatypeType] | +| Sources/deduplication/def.swift:26:30:26:30 | B | GenericTypeParamDecl | B.Type [MetatypeType] | +| Sources/deduplication/def.swift:26:33:26:33 | C | GenericTypeParamDecl | C.Type [MetatypeType] | +| Sources/deduplication/def.swift:26:36:26:39 | _ | ParamDecl | A [GenericTypeParamType] | +| Sources/deduplication/def.swift:26:42:26:45 | _ | ParamDecl | B [GenericTypeParamType] | +| Sources/deduplication/def.swift:26:48:26:51 | _ | ParamDecl | C [GenericTypeParamType] | +| Sources/deduplication/def.swift:28:1:28:21 | Protocol1 | ProtocolDecl | Protocol1.Protocol [MetatypeType] | +| Sources/deduplication/def.swift:29:1:31:1 | Protocol2 | ProtocolDecl | Protocol2.Protocol [MetatypeType] | +| Sources/deduplication/def.swift:30:5:30:20 | Associated | AssociatedTypeDecl | Self.Associated.Type [MetatypeType] | +| Sources/deduplication/def.swift:32:1:32:14 | Class | ClassDecl | Class.Type [MetatypeType] | +| Sources/deduplication/def.swift:32:7:32:7 | Class.deinit() | Deinitializer | (Class) -> () -> () [FunctionType] | +| Sources/deduplication/def.swift:32:7:32:7 | Class.init() | Initializer | (Class.Type) -> () -> Class [FunctionType] | +| Sources/deduplication/def.swift:32:7:32:7 | self | ParamDecl | Class [ClassType] | +| Sources/deduplication/def.swift:32:7:32:7 | self | ParamDecl | Class [ClassType] | +| Sources/deduplication/def.swift:34:1:34:128 | def_generic_function_with_conformance(_:_:_:) | NamedFunction | (A, B, C) -> () [GenericFunctionType] | +| Sources/deduplication/def.swift:34:44:34:60 | A | GenericTypeParamDecl | A.Type [MetatypeType] | +| Sources/deduplication/def.swift:34:71:34:75 | B | GenericTypeParamDecl | B.Type [MetatypeType] | +| Sources/deduplication/def.swift:34:82:34:82 | C | GenericTypeParamDecl | C.Type [MetatypeType] | +| Sources/deduplication/def.swift:34:85:34:88 | _ | ParamDecl | A [GenericTypeParamType] | +| Sources/deduplication/def.swift:34:91:34:94 | _ | ParamDecl | B [GenericTypeParamType] | +| Sources/deduplication/def.swift:34:97:34:100 | _ | ParamDecl | C [GenericTypeParamType] | +| Sources/deduplication/def.swift:36:1:36:18 | extension of Class | ExtensionDecl | - | +| Sources/deduplication/use.swift:1:1:1:15 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/use.swift:1:5:1:5 | use_int | ConcreteVarDecl | Int [StructType] | +| Sources/deduplication/use.swift:2:1:2:32 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/use.swift:2:5:2:5 | use_instantiated_generic | ConcreteVarDecl | Generic [BoundGenericStructType] | +| Sources/deduplication/use.swift:3:1:3:83 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/use.swift:3:5:3:5 | answer | ConcreteVarDecl | Int [StructType] | +| Sources/deduplication/use.swift:4:1:4:20 | var ... = ... | PatternBindingDecl | - | +| Sources/deduplication/use.swift:4:5:4:5 | use_function | ConcreteVarDecl | (Int) -> Int [FunctionType] | +| Sources/deduplication/use.swift:5:1:5:66 | use_generic_function_type(_:_:_:) | NamedFunction | (AA, BB, CC) -> () [GenericFunctionType] | +| Sources/deduplication/use.swift:5:32:5:32 | AA | GenericTypeParamDecl | AA.Type [MetatypeType] | +| Sources/deduplication/use.swift:5:36:5:36 | BB | GenericTypeParamDecl | BB.Type [MetatypeType] | +| Sources/deduplication/use.swift:5:40:5:40 | CC | GenericTypeParamDecl | CC.Type [MetatypeType] | +| Sources/deduplication/use.swift:5:44:5:47 | _ | ParamDecl | AA [GenericTypeParamType] | +| Sources/deduplication/use.swift:5:51:5:54 | _ | ParamDecl | BB [GenericTypeParamType] | +| Sources/deduplication/use.swift:5:58:5:61 | _ | ParamDecl | CC [GenericTypeParamType] | +| Sources/deduplication/use.swift:6:1:6:45 | use_async_function_type(_:) | NamedFunction | (Int) async -> () [FunctionType] | +| Sources/deduplication/use.swift:6:30:6:33 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/use.swift:7:1:7:49 | use_throwing_function_type(_:) | NamedFunction | (Int) throws -> () [FunctionType] | +| Sources/deduplication/use.swift:7:33:7:36 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/use.swift:8:1:8:61 | use_async_throwing_function_type(_:) | NamedFunction | (Int) async throws -> () [FunctionType] | +| Sources/deduplication/use.swift:8:39:8:42 | _ | ParamDecl | Int [StructType] | +| Sources/deduplication/use.swift:9:1:9:150 | use_generic_function_with_conformance_type(_:_:_:) | NamedFunction | (AA, BB, CC) -> () [GenericFunctionType] | +| Sources/deduplication/use.swift:9:49:9:49 | AA | GenericTypeParamDecl | AA.Type [MetatypeType] | +| Sources/deduplication/use.swift:9:53:9:53 | BB | GenericTypeParamDecl | BB.Type [MetatypeType] | +| Sources/deduplication/use.swift:9:57:9:57 | CC | GenericTypeParamDecl | CC.Type [MetatypeType] | +| Sources/deduplication/use.swift:9:61:9:64 | _ | ParamDecl | AA [GenericTypeParamType] | +| Sources/deduplication/use.swift:9:68:9:71 | _ | ParamDecl | BB [GenericTypeParamType] | +| Sources/deduplication/use.swift:9:75:9:78 | _ | ParamDecl | CC [GenericTypeParamType] | +| Sources/deduplication/use.swift:11:1:11:18 | extension of Class | ExtensionDecl | - | diff --git a/swift/integration-tests/posix-only/deduplication/Decls.ql b/swift/integration-tests/posix-only/deduplication/Decls.ql index 9f779f93b3e..b3c96fa8305 100644 --- a/swift/integration-tests/posix-only/deduplication/Decls.ql +++ b/swift/integration-tests/posix-only/deduplication/Decls.ql @@ -1,6 +1,15 @@ import swift import Relevant -from Decl d -where relevant(d) -select d, d.getPrimaryQlClasses() +from Decl d, string type +where + relevant(d) and + ( + not exists(d.(ValueDecl).getInterfaceType()) and type = "-" + or + exists(Type t | + t = d.(ValueDecl).getInterfaceType() and + type = t.toString() + " [" + t.getPrimaryQlClasses() + "]" + ) + ) +select d, d.getPrimaryQlClasses(), type diff --git a/swift/integration-tests/posix-only/deduplication/Package.swift b/swift/integration-tests/posix-only/deduplication/Package.swift index fa7a3e04e9d..4ebc5d674f4 100644 --- a/swift/integration-tests/posix-only/deduplication/Package.swift +++ b/swift/integration-tests/posix-only/deduplication/Package.swift @@ -1,10 +1,13 @@ -// swift-tools-version: 5.5 +// swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "deduplication", + platforms: [ + .macOS(.v10_15), + ], products: [ .library( name: "deduplication", diff --git a/swift/integration-tests/posix-only/deduplication/Sources/deduplication/def.swift b/swift/integration-tests/posix-only/deduplication/Sources/deduplication/def.swift index dcb3f69343c..a8f3c8beb58 100644 --- a/swift/integration-tests/posix-only/deduplication/Sources/deduplication/def.swift +++ b/swift/integration-tests/posix-only/deduplication/Sources/deduplication/def.swift @@ -1,7 +1,36 @@ -var x = 42 +var def_int = 42 -struct Generic {} +struct Generic { + func def_generic_method(t: T, u: U, v: V) -> T { return t; } +} -var instantiated_generic = Generic() +var def_instantiated_generic = Generic() -func function(_: Int) -> Int { return 42 } +func def_function(_: Int) -> Int { return 42 } + +func def_function_overloaded_on_return(_: Int, _: Double) -> Int { return 0 } +func def_function_overloaded_on_return(_: Int, _: Double) -> Double { return 0.0 } + +func def_function_overloaded_on_parameter_type(_: Int) {} +func def_function_overloaded_on_parameter_type(_: Double) {} + +func def_function_overloaded_on_parameter_label(one x: Int) {} +func def_function_overloaded_on_parameter_label(two x: Int) {} + +func def_throwing_function(_: Int) throws {} +func def_rethrowing_function(_: () throws -> ()) rethrows {} +func def_async_function(_: Int) async {} +func def_async_throwing_function(_: Int) async throws {} +func def_async_rethrowing_function(_: () throws -> ()) async rethrows {} + +func def_generic_function(_: A, _: B, _: C) {} + +protocol Protocol1 {} +protocol Protocol2 { + associatedtype Associated; +} +class Class {} + +func def_generic_function_with_conformance(_: A, _: B, _: C) where C == A.Associated {} + +extension Class {} diff --git a/swift/integration-tests/posix-only/deduplication/Sources/deduplication/use.swift b/swift/integration-tests/posix-only/deduplication/Sources/deduplication/use.swift index 02255ecb7b0..93a715833fd 100644 --- a/swift/integration-tests/posix-only/deduplication/Sources/deduplication/use.swift +++ b/swift/integration-tests/posix-only/deduplication/Sources/deduplication/use.swift @@ -1,3 +1,11 @@ -let use_x = x -let use_instantiated_generic = instantiated_generic -let use_function = function +let use_int = def_int +let use_instantiated_generic = def_instantiated_generic +let answer = use_instantiated_generic.def_generic_method(t: 42, u: "hello", v: 0.0) +let use_function = def_function +func use_generic_function_type(_: AA, _: BB, _: CC) {} +func use_async_function_type(_: Int) async {} +func use_throwing_function_type(_: Int) throws {} +func use_async_throwing_function_type(_: Int) async throws {} +func use_generic_function_with_conformance_type(_: AA, _: BB, _: CC) where AA: Protocol1, AA: Protocol2, BB: Class, CC == AA.Associated {} + +extension Class {} diff --git a/swift/integration-tests/posix-only/deduplication/Types.expected b/swift/integration-tests/posix-only/deduplication/Types.expected index b610fd5886a..70812fa230e 100644 --- a/swift/integration-tests/posix-only/deduplication/Types.expected +++ b/swift/integration-tests/posix-only/deduplication/Types.expected @@ -1,10 +1,20 @@ -| (Int) -> Int | FunctionType | function(_:) | -| (Int) -> Int | FunctionType | use_function | -| (Generic.Type) -> () -> Generic | GenericFunctionType | Generic.init() | -| Generic | BoundGenericStructType | instantiated_generic | -| Generic | BoundGenericStructType | use_instantiated_generic | -| Generic | BoundGenericStructType | self | -| Generic.Type | MetatypeType | Generic | -| Int | StructType | _, x | -| Int | StructType | use_x | -| T.Type | MetatypeType | T | +| (() throws -> ()) async throws -> () | FunctionType | def_async_rethrowing_function(_:) | | +| (() throws -> ()) throws -> () | FunctionType | def_rethrowing_function(_:) | | +| (Double) -> () | FunctionType | def_function_overloaded_on_parameter_type(_:) | | +| (Int) -> () | FunctionType | def_function_overloaded_on_parameter_label(one:), def_function_overloaded_on_parameter_label(two:), def_function_overloaded_on_parameter_type(_:) | | +| (Int) -> Int | FunctionType | def_function(_:), use_function | | +| (Int) async -> () | FunctionType | def_async_function(_:), use_async_function_type(_:) | | +| (Int) async throws -> () | FunctionType | def_async_throwing_function(_:), use_async_throwing_function_type(_:) | | +| (Int) throws -> () | FunctionType | def_throwing_function(_:), use_throwing_function_type(_:) | | +| (Int, Double) -> Double | FunctionType | def_function_overloaded_on_return(_:_:) | | +| (Int, Double) -> Int | FunctionType | def_function_overloaded_on_return(_:_:) | | +| (A, B, C) -> () | GenericFunctionType | def_generic_function_with_conformance(_:_:_:) | <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2 where \u03c4_0_0 : Protocol1, \u03c4_0_0 : Protocol2, \u03c4_0_1 : Class, \u03c4_0_2 == \u03c4_0_0.Associated> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | +| (A, B, C) -> () | GenericFunctionType | def_generic_function(_:_:_:) | <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | +| (AA, BB, CC) -> () | GenericFunctionType | use_generic_function_with_conformance_type(_:_:_:) | <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2 where \u03c4_0_0 : Protocol1, \u03c4_0_0 : Protocol2, \u03c4_0_1 : Class, \u03c4_0_2 == \u03c4_0_0.Associated> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | +| (AA, BB, CC) -> () | GenericFunctionType | use_generic_function_type(_:_:_:) | <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | +| (Generic) -> (T, U, V) -> T | GenericFunctionType | def_generic_method(t:u:v:) | <\u03c4_0_0, \u03c4_1_0, \u03c4_1_1> (Generic<\u03c4_0_0>) -> (\u03c4_0_0, \u03c4_1_0, \u03c4_1_1) -> \u03c4_0_0 | +| <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2 where \u03c4_0_0 : Protocol1, \u03c4_0_0 : Protocol2, \u03c4_0_1 : Class, \u03c4_0_2 == \u03c4_0_0.Associated> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | GenericFunctionType | def_generic_function_with_conformance(_:_:_:), use_generic_function_with_conformance_type(_:_:_:) | | +| <\u03c4_0_0, \u03c4_0_1, \u03c4_0_2> (\u03c4_0_0, \u03c4_0_1, \u03c4_0_2) -> () | GenericFunctionType | def_generic_function(_:_:_:), use_generic_function_type(_:_:_:) | | +| <\u03c4_0_0, \u03c4_1_0, \u03c4_1_1> (Generic<\u03c4_0_0>) -> (\u03c4_0_0, \u03c4_1_0, \u03c4_1_1) -> \u03c4_0_0 | GenericFunctionType | def_generic_method(t:u:v:) | | +| Generic | BoundGenericStructType | def_instantiated_generic, use_instantiated_generic | | +| Int | StructType | def_int, use_int | | diff --git a/swift/integration-tests/posix-only/deduplication/Types.ql b/swift/integration-tests/posix-only/deduplication/Types.ql index 22e1b476b3c..b5e6c1601fe 100644 --- a/swift/integration-tests/posix-only/deduplication/Types.ql +++ b/swift/integration-tests/posix-only/deduplication/Types.ql @@ -1,7 +1,15 @@ import swift import Relevant -from Type t, string decls +from Type t, string decls, string canonical where - decls = strictconcat(ValueDecl d | relevant(d) and t = d.getInterfaceType() | d.toString(), ", ") -select t, t.getPrimaryQlClasses(), decls + decls = + strictconcat(ValueDecl d | + relevant(d) and + t = [d.getInterfaceType(), d.getInterfaceType().getCanonicalType()] and + d.toString().matches(["use_%", "def_%"]) + | + d.toString(), ", " + ) and + if t = t.getCanonicalType() then canonical = "" else canonical = t.getCanonicalType().toString() +select t, t.getPrimaryQlClasses(), decls, canonical diff --git a/swift/ql/lib/CHANGELOG.md b/swift/ql/lib/CHANGELOG.md index 433d053b3ab..1f8dabee28d 100644 --- a/swift/ql/lib/CHANGELOG.md +++ b/swift/ql/lib/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.2.2 + +### Major Analysis Improvements + +* Added `DataFlow::ArrayContent`, which will provide more accurate flow through arrays. + +### Minor Analysis Improvements + +* Flow through forced optional unwrapping (`!`) is modelled more accurately. +* Added flow models for `Sequence.withContiguousStorageIfAvailable`. +* Added taint flow for `NSUserActivity.referrerURL`. + ## 0.2.1 ### New Features diff --git a/swift/ql/lib/change-notes/released/0.2.2.md b/swift/ql/lib/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..9ffc9a4d3e8 --- /dev/null +++ b/swift/ql/lib/change-notes/released/0.2.2.md @@ -0,0 +1,11 @@ +## 0.2.2 + +### Major Analysis Improvements + +* Added `DataFlow::ArrayContent`, which will provide more accurate flow through arrays. + +### Minor Analysis Improvements + +* Flow through forced optional unwrapping (`!`) is modelled more accurately. +* Added flow models for `Sequence.withContiguousStorageIfAvailable`. +* Added taint flow for `NSUserActivity.referrerURL`. diff --git a/swift/ql/lib/codeql-pack.release.yml b/swift/ql/lib/codeql-pack.release.yml index df29a726bcc..16a06790aa8 100644 --- a/swift/ql/lib/codeql-pack.release.yml +++ b/swift/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.1 +lastReleaseVersion: 0.2.2 diff --git a/swift/ql/src/queries/ide-contextual-queries/IDEContextual.qll b/swift/ql/lib/codeql/IDEContextual.qll similarity index 100% rename from swift/ql/src/queries/ide-contextual-queries/IDEContextual.qll rename to swift/ql/lib/codeql/IDEContextual.qll diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index 4ec5d691256..570e1882261 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -1235,14 +1235,6 @@ module Exprs { } } - private class InOutTree extends AstStandardPostOrderTree { - override InOutExpr ast; - - final override ControlFlowElement getChildElement(int i) { - i = 0 and result.asAstNode() = ast.getSubExpr().getFullyConverted() - } - } - private class SubscriptTree extends AstControlFlowTree { override SubscriptExpr ast; @@ -1749,7 +1741,8 @@ module Exprs { module Conversions { class ConversionOrIdentity = - Synth::TIdentityExpr or Synth::TExplicitCastExpr or Synth::TImplicitConversionExpr; + Synth::TIdentityExpr or Synth::TExplicitCastExpr or Synth::TImplicitConversionExpr or + Synth::TInOutExpr; abstract class ConversionOrIdentityTree extends AstStandardPostOrderTree { ConversionOrIdentityTree() { ast instanceof ConversionOrIdentity } @@ -1780,6 +1773,12 @@ module Exprs { override predicate convertsFrom(Expr e) { ast.convertsFrom(e) } } + + private class InOutTree extends ConversionOrIdentityTree { + override InOutExpr ast; + + override predicate convertsFrom(Expr e) { ast.convertsFrom(e) } + } } } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql b/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql index 163e7f7a1d0..fa25c039263 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql +++ b/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql @@ -7,9 +7,52 @@ * @tags ide-contextual-queries/print-cfg */ +private import codeql.IDEContextual private import codeql.swift.controlflow.ControlFlowGraph private import codeql.swift.controlflow.internal.ControlFlowGraphImpl::TestOutput -class MyRelevantNode extends RelevantNode { - MyRelevantNode() { any() } +/** + * Gets the source file to generate a CFG from. + */ +external string selectedSourceFile(); + +/** + * Gets the source line to generate a CFG from. + */ +external string selectedSourceLine(); + +/** + * Gets the source column to generate a CFG from. + */ +external string selectedSourceColumn(); + +bindingset[file, line, column] +private CfgScope smallestEnclosingScope(File file, int line, int column) { + result = + min(Location loc, CfgScope scope | + loc = scope.getLocation() and + ( + loc.getStartLine() < line + or + loc.getStartLine() = line and loc.getStartColumn() <= column + ) and + ( + loc.getEndLine() > line + or + loc.getEndLine() = line and loc.getEndColumn() >= column + ) and + loc.getFile() = file + | + scope + order by + loc.getStartLine() desc, loc.getStartColumn() desc, loc.getEndLine(), loc.getEndColumn() + ) +} + +class MyRelevantNode extends RelevantNode { + MyRelevantNode() { + this.getScope() = + smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()), + selectedSourceLine().toInt(), selectedSourceColumn().toInt()) + } } diff --git a/swift/ql/lib/codeql/swift/dataflow/DataFlow.qll b/swift/ql/lib/codeql/swift/dataflow/DataFlow.qll index 2876d4b5d64..b267b74d328 100644 --- a/swift/ql/lib/codeql/swift/dataflow/DataFlow.qll +++ b/swift/ql/lib/codeql/swift/dataflow/DataFlow.qll @@ -3,6 +3,8 @@ * global (inter-procedural) data flow analyses. */ module DataFlow { - import internal.DataFlow + private import internal.DataFlowImplSpecific + private import codeql.dataflow.DataFlow + import DataFlowMake import internal.DataFlowImpl1 } diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll index ba680b1b138..5778a760fef 100644 --- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll +++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll @@ -476,9 +476,22 @@ private predicate parseField(AccessPathToken c, Content::FieldContent f) { ) } +private predicate parseEnum(AccessPathToken c, Content::EnumContent f) { + c.getName() = "EnumElement" and + c.getAnArgument() = f.getSignature() + or + c.getName() = "OptionalSome" and + f.getSignature() = "some:0" +} + /** Holds if the specification component parses as a `Content`. */ predicate parseContent(AccessPathToken component, Content content) { parseField(component, content) + or + parseEnum(component, content) + or + component.getName() = "ArrayElement" and + content instanceof Content::ArrayContent } cached diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll deleted file mode 100644 index 03975c6a54a..00000000000 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Provides an implementation of global (interprocedural) data flow. This file - * re-exports the local (intraprocedural) data flow analysis from - * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed - * through the `Global` and `GlobalWithState` modules. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -import DataFlowImplSpecific::Public -import DataFlowImplCommonPublic -private import DataFlowImpl - -/** An input configuration for data flow. */ -signature module ConfigSig { - /** - * Holds if `source` is a relevant data flow source. - */ - predicate isSource(Node source); - - /** - * Holds if `sink` is a relevant data flow sink. - */ - predicate isSink(Node sink); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or isAdditionalFlowStep(_, node) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** An input configuration for data flow using flow state. */ -signature module StateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - default predicate isBarrier(Node node) { none() } - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - default predicate isBarrier(Node node, FlowState state) { none() } - - /** Holds if data flow into `node` is prohibited. */ - default predicate isBarrierIn(Node node) { none() } - - /** Holds if data flow out of `node` is prohibited. */ - default predicate isBarrierOut(Node node) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - default predicate isAdditionalFlowStep(Node node1, Node node2) { none() } - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - default predicate allowImplicitRead(Node node, ContentSet c) { none() } - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - default predicate neverSkip(Node node) { - isAdditionalFlowStep(node, _) or - isAdditionalFlowStep(_, node) or - isAdditionalFlowStep(node, _, _, _) or - isAdditionalFlowStep(_, _, node, _) - } - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - default int fieldFlowBranchLimit() { result = 2 } - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - default FlowFeature getAFeature() { none() } - - /** Holds if sources should be grouped in the result of `flowPath`. */ - default predicate sourceGrouping(Node source, string sourceGroup) { none() } - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - default predicate sinkGrouping(Node sink, string sinkGroup) { none() } - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - default predicate includeHiddenNodes() { none() } -} - -/** - * Gets the exploration limit for `partialFlow` and `partialFlowRev` - * measured in approximate number of interprocedural steps. - */ -signature int explorationLimitSig(); - -/** - * The output of a global data flow computation. - */ -signature module GlobalFlowSig { - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode; - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink); - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink); - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink); -} - -/** - * Constructs a global data flow computation. - */ -module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - } - - import Impl -} - -/** DEPRECATED: Use `Global` instead. */ -deprecated module Make implements GlobalFlowSig { - import Global -} - -/** - * Constructs a global data flow computation using flow state. - */ -module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - } - - import Impl -} - -/** DEPRECATED: Use `GlobalWithState` instead. */ -deprecated module MakeWithState implements GlobalFlowSig { - import GlobalWithState -} - -signature class PathNodeSig { - /** Gets a textual representation of this element. */ - string toString(); - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); - - /** Gets the underlying `Node`. */ - Node getNode(); -} - -signature module PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - predicate edges(PathNode a, PathNode b); - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - predicate nodes(PathNode n, string key, string val); - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out); -} - -/** - * Constructs a `PathGraph` from two `PathGraph`s by disjoint union. - */ -module MergePathGraph< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig Graph1, - PathGraphSig Graph2> -{ - private newtype TPathNode = - TPathNode1(PathNode1 p) or - TPathNode2(PathNode2 p) - - /** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */ - class PathNode extends TPathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { this = TPathNode1(result) } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { this = TPathNode2(result) } - - /** Gets a textual representation of this element. */ - string toString() { - result = this.asPathNode1().toString() or - result = this.asPathNode2().toString() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { - result = this.asPathNode1().getNode() or - result = this.asPathNode2().getNode() - } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { - Graph1::edges(a.asPathNode1(), b.asPathNode1()) or - Graph2::edges(a.asPathNode2(), b.asPathNode2()) - } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - Graph1::nodes(n.asPathNode1(), key, val) or - Graph2::nodes(n.asPathNode2(), key, val) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or - Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2()) - } - } -} - -/** - * Constructs a `PathGraph` from three `PathGraph`s by disjoint union. - */ -module MergePathGraph3< - PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3, - PathGraphSig Graph1, PathGraphSig Graph2, PathGraphSig Graph3> -{ - private module MergedInner = MergePathGraph; - - private module Merged = - MergePathGraph; - - /** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */ - class PathNode instanceof Merged::PathNode { - /** Gets this as a projection on the first given `PathGraph`. */ - PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() } - - /** Gets this as a projection on the second given `PathGraph`. */ - PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() } - - /** Gets this as a projection on the third given `PathGraph`. */ - PathNode3 asPathNode3() { result = super.asPathNode2() } - - /** Gets a textual representation of this element. */ - string toString() { result = super.toString() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - Node getNode() { result = super.getNode() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph = Merged::PathGraph; -} diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index 29561b0f0a6..532f0def116 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -1,4744 +1,3 @@ -/** - * INTERNAL: Do not use. - * - * Provides an implementation of global (interprocedural) data flow. - */ - -private import DataFlowImplCommon -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -private import DataFlowImplCommonPublic -private import codeql.util.Unit -private import codeql.util.Option -import DataFlow - -/** - * An input configuration for data flow using flow state. This signature equals - * `StateConfigSig`, but requires explicit implementation of all predicates. - */ -signature module FullStateConfigSig { - bindingset[this] - class FlowState; - - /** - * Holds if `source` is a relevant data flow source with the given initial - * `state`. - */ - predicate isSource(Node source, FlowState state); - - /** - * Holds if `sink` is a relevant data flow sink accepting `state`. - */ - predicate isSink(Node sink, FlowState state); - - /** - * Holds if data flow through `node` is prohibited. This completely removes - * `node` from the data flow graph. - */ - predicate isBarrier(Node node); - - /** - * Holds if data flow through `node` is prohibited when the flow state is - * `state`. - */ - predicate isBarrier(Node node, FlowState state); - - /** Holds if data flow into `node` is prohibited. */ - predicate isBarrierIn(Node node); - - /** Holds if data flow out of `node` is prohibited. */ - predicate isBarrierOut(Node node); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - */ - predicate isAdditionalFlowStep(Node node1, Node node2); - - /** - * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. - * This step is only applicable in `state1` and updates the flow state to `state2`. - */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); - - /** - * Holds if an arbitrary number of implicit read steps of content `c` may be - * taken at `node`. - */ - predicate allowImplicitRead(Node node, ContentSet c); - - /** - * Holds if `node` should never be skipped over in the `PathGraph` and in path - * explanations. - */ - predicate neverSkip(Node node); - - /** - * Gets the virtual dispatch branching limit when calculating field flow. - * This can be overridden to a smaller value to improve performance (a - * value of 0 disables field flow), or a larger value to get more results. - */ - int fieldFlowBranchLimit(); - - /** - * Gets a data flow configuration feature to add restrictions to the set of - * valid flow paths. - * - * - `FeatureHasSourceCallContext`: - * Assume that sources have some existing call context to disallow - * conflicting return-flow directly following the source. - * - `FeatureHasSinkCallContext`: - * Assume that sinks have some existing call context to disallow - * conflicting argument-to-parameter flow directly preceding the sink. - * - `FeatureEqualSourceSinkCallContext`: - * Implies both of the above and additionally ensures that the entire flow - * path preserves the call context. - * - * These features are generally not relevant for typical end-to-end data flow - * queries, but should only be used for constructing paths that need to - * somehow be pluggable in another path context. - */ - FlowFeature getAFeature(); - - /** Holds if sources should be grouped in the result of `flowPath`. */ - predicate sourceGrouping(Node source, string sourceGroup); - - /** Holds if sinks should be grouped in the result of `flowPath`. */ - predicate sinkGrouping(Node sink, string sinkGroup); - - /** - * Holds if hidden nodes should be included in the data flow graph. - * - * This feature should only be used for debugging or when the data flow graph - * is not visualized (as it is in a `path-problem` query). - */ - predicate includeHiddenNodes(); -} - -/** - * Provides default `FlowState` implementations given a `StateConfigSig`. - */ -module DefaultState { - class FlowState = Unit; - - predicate isSource(Node source, FlowState state) { Config::isSource(source) and exists(state) } - - predicate isSink(Node sink, FlowState state) { Config::isSink(sink) and exists(state) } - - predicate isBarrier(Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { - none() - } -} - -/** - * Constructs a data flow computation given a full input configuration. - */ -module Impl { - private class FlowState = Config::FlowState; - - private newtype TNodeEx = - TNodeNormal(Node n) or - TNodeImplicitRead(Node n, boolean hasRead) { - Config::allowImplicitRead(n, _) and hasRead = [false, true] - } - - private class NodeEx extends TNodeEx { - string toString() { - result = this.asNode().toString() - or - exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") - } - - Node asNode() { this = TNodeNormal(result) } - - predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } - - Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } - - pragma[nomagic] - private DataFlowCallable getEnclosingCallable0() { - nodeEnclosingCallable(this.projectToNode(), result) - } - - pragma[inline] - DataFlowCallable getEnclosingCallable() { - pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) - } - - pragma[nomagic] - private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } - - pragma[inline] - DataFlowType getDataFlowType() { - pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) - } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - private class ArgNodeEx extends NodeEx { - ArgNodeEx() { this.asNode() instanceof ArgNode } - } - - private class ParamNodeEx extends NodeEx { - ParamNodeEx() { this.asNode() instanceof ParamNode } - - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.asNode().(ParamNode).isParameterOf(c, pos) - } - - ParameterPosition getPosition() { this.isParameterOf(_, result) } - } - - private class RetNodeEx extends NodeEx { - RetNodeEx() { this.asNode() instanceof ReturnNodeExt } - - ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } - - ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } - } - - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - Config::isSource(n, _) - ) - } - - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierOut(n) and - Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not Config::isSource(n, _) - or - Config::isBarrierOut(n) and - not Config::isSink(n, _) - ) - } - - pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | Config::isBarrier(n, state)) - } - - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - Config::isSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - pragma[nomagic] - private predicate sinkNode(NodeEx node, FlowState state) { - Config::isSink(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } - - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - pragma[inline] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } - - pragma[nomagic] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - isUnreachableInCallCached(n.asNode(), cc.getCall()) - } - - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.asNode() = n and - node2.isImplicitReadNode(n, false) and - not fullBarrier(node1) - ) - } - - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - or - exists(Node n | - Config::allowImplicitRead(n, _) and - node1.isImplicitReadNode(n, true) and - node2.asNode() = n and - not fullBarrier(node2) - ) - } - - private predicate additionalLocalStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not stateBarrier(node1, s1) and - not stateBarrier(node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } - - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n, true) and - node1.isImplicitReadNode(n, _) and - Config::allowImplicitRead(n, c) - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentCached(n.asNode(), cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } - - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } - - pragma[nomagic] - private predicate storeEx( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - store(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode()), - contentType, containerType) and - hasReadStep(c) and - stepFilter(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { - viableReturnPosOut(call, pos, out.asNode()) - } - - pragma[nomagic] - private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - viableParamArg(call, p.asNode(), arg.asNode()) - } - - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { Config::fieldFlowBranchLimit() >= 1 } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate sourceCallCtx(CallContext cc) { - if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfCached(p.asNode()) - ) - } - - private module Stage1 implements StageSig { - class Ap = Unit; - - private class Cc = boolean; - - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node) or - additionalLocalFlowStep(mid, node) or - additionalLocalStateStep(mid, _, node, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node) or - additionalJumpStateStep(mid, _, node, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - storeEx(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call | - fwdFlowOutFromArg(call, node) and - fwdFlowIsEntered(call, cc) - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc) { fwdFlowIn(call, _, cc, _) } - - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } - - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } - - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } - - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - storeEx(mid, c, node, _, _) - ) - } - - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } - - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out) { - fwdFlowOut(call, out, true) - } - - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) - | - fwdFlow(node1) - ) - } - - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } - - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - private predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } - - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - exists(FlowState state | - fwdFlow(node) and - sinkNode(node, state) and - fwdFlowState(state) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - ) - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid) or - additionalLocalFlowStep(node, mid) or - additionalLocalStateStep(node, _, mid, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid) or - additionalJumpStateStep(node, _, mid, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call | - revFlowInToReturn(call, node) and - revFlowIsReturned(call, toReturn) - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - storeEx(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } - - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } - - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } - - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - fwdFlowIn(call, arg, _, p) - } - - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ArgNodeEx arg, boolean toReturn) { - exists(ParamNodeEx p | - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg) { - revFlowIn(call, arg, true) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, out) - ) - } - - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } - - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - storeEx(node1, c, node2, contentType, containerType) and - exists(ap1) - } - - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { - revFlow(node) and - exists(ap) - } - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } - - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } - - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - exists(ap) and - parameterFlowThroughAllowed(p, kind) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() and - exists(argAp) and - exists(ap) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, boolean toReturn | - revFlow(arg, toReturn) and - revFlowInToReturn(call, arg) and - revFlowIsReturned(call, toReturn) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) - } - /* End: Stage 1 logic. */ - } - - pragma[noinline] - private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - localFlowStepEx(node1, node2) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2) { - Stage1::revFlow(node2) and - additionalLocalFlowStep(node1, node2) - } - - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } - - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } - - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(NodeEx n1) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n1, _, n) or flowIntoCallNodeCand1(_, n1, n)) - + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } - - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(NodeEx n2) { - result = - strictcount(NodeEx n | flowOutOfCallNodeCand1(_, n, _, n2) or flowIntoCallNodeCand1(_, n, n2)) - + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } - - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int b, int j | - b = branch(ret) and - j = join(out) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if b.minimum(j) <= Config::fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } - - private signature module StageSig { - class Ap; - - predicate revFlow(NodeEx node); - - predicate revFlowAp(NodeEx node, Ap ap); - - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); - - predicate callMayFlowThroughRev(DataFlowCall call); - - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap); - - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind); - - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ); - - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - } - - private module MkStage { - class ApApprox = PrevStage::Ap; - - signature module StageParam { - class Typ { - string toString(); - } - - class Ap; - - class ApNil extends Ap; - - bindingset[result, ap] - ApApprox getApprox(Ap ap); - - Typ getTyp(DataFlowType t); - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail); - - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; - - ApHeadContent getHeadContent(Ap ap); - - ApHeadContent projectToHeadContent(Content c); - - class ApOption; - - ApOption apNone(); - - ApOption apSome(Ap ap); - - class Cc; - - class CcCall extends Cc; - - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - class LocalCc; - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc); - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc - ); - - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ); - - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ); - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType); - } - - module Stage implements StageSig { - import Param - - /* Begin: Stage logic. */ - private module TypOption = Option; - - private class TypOption = TypOption::Option; - - pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) - } - - pragma[nomagic] - private predicate flowIntoCallApa( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa - ) { - flowIntoCall(call, arg, p, allowsFieldFlow) and - PrevStage::revFlowAp(p, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(arg, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowOutOfCallApa( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow, - ApApprox apa - ) { - flowOutOfCall(call, ret, kind, out, allowsFieldFlow) and - PrevStage::revFlowAp(out, pragma[only_bind_into](apa)) and - PrevStage::revFlowAp(ret, pragma[only_bind_into](apa)) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - ApApprox argApa, ApApprox apa - ) { - exists(ReturnKindExt kind | - flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) and - matchesCall(ccc, call) - ) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` and `argAp` record the - * corresponding parameter position and access path of that argument, respectively. - */ - pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa) - } - - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa - ) { - fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) - } - - pragma[nomagic] - private predicate typeStrengthen(Typ t0, Ap ap, Typ t) { - fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - argT instanceof TypOption::None and - argAp = apNone() and - summaryCtx = TParamNodeNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, localCc) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, t, ap, apa) and - jumpStepEx(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() - ) - or - exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, _, _, ap, apa) and - additionalJumpStep(mid, node) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and - cc = ccNone() and - summaryCtx = TParamNodeNone() and - argT instanceof TypOption::None and - argAp = apNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // store - exists(Content c, Typ t0, Ap ap0 | - fwdFlowStore(_, t0, ap0, c, t, node, state, cc, summaryCtx, argT, argAp) and - ap = apCons(c, t0, ap0) and - apa = getApprox(ap) - ) - or - // read - exists(Typ t0, Ap ap0, Content c | - fwdFlowRead(t0, ap0, c, _, node, state, cc, summaryCtx, argT, argAp) and - fwdFlowConsCand(t0, ap0, c, t, ap) and - apa = getApprox(ap) - ) - or - // flow into a callable - fwdFlowIn(_, node, state, _, cc, _, _, _, t, ap, apa) and - if PrevStage::parameterMayFlowThrough(node, apa) - then ( - summaryCtx = TParamNodeSome(node.asNode()) and - argT = TypOption::some(t) and - argAp = apSome(ap) - ) else ( - summaryCtx = TParamNodeNone() and argT instanceof TypOption::None and argAp = apNone() - ) - or - // flow out of a callable - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, summaryCtx, argT, argAp, t, ap, apa) and - flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa) and - inner = ret.getEnclosingCallable() and - cc = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - or - // flow through a callable - exists( - DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, - ApApprox innerArgApa - | - fwdFlowThrough(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, Content c, Typ t2, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(DataFlowType contentType, DataFlowType containerType, ApApprox apa1 | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t1, ap1, apa1) and - PrevStage::storeStepCand(node1, apa1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - typecheckStore(t1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and - cons = apCons(c, t1, tail) - or - exists(Typ t0 | - typeStrengthen(t0, cons, t2) and - fwdFlowConsCand(t0, cons, c, t1, tail) - ) - } - - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } - - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Typ t, Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - ParamNodeOption summaryCtx, TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Typ argT, Ap argAp, - ApApprox argApa, Typ t, Ap ap, ApApprox apa - ) { - exists(ReturnKindExt kind | - fwdFlow(pragma[only_bind_into](ret), state, ccc, - TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT), - pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and - kind = ret.getKind() and - parameterFlowThroughAllowed(summaryCtx, kind) and - argApa = getApprox(argAp) and - PrevStage::returnMayFlowThrough(ret, argApa, apa, kind) - ) - } - - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgApa, t, - ap, apa) and - fwdFlowIsEntered(call, cc, ccc, summaryCtx, argT, argAp, innerSummaryCtx, innerArgT, - innerArgAp) - } - - pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx, - TypOption argT, ApOption argAp, Typ t, Ap ap, ApApprox apa, RetNodeEx ret, - ApApprox innerArgApa - ) { - fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argT, argAp, t, ap, apa, ret, _, _, _, - innerArgApa) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, ParamNodeEx p, Typ t, Ap ap - ) { - exists(ApApprox apa | - fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argT, argAp, t, ap, - pragma[only_bind_into](apa)) and - PrevStage::parameterMayFlowThrough(p, apa) and - PrevStage::callMayFlowThroughRev(call) - ) - } - - pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Typ t1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, t1, ap1, c, _, node2, _, _, _, _, _) and - ap2 = apCons(c, t1, ap1) and - readStepFwd(_, ap2, c, _, _) - } - - pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - exists(Typ t1 | - fwdFlowRead(t1, ap1, c, n1, n2, _, _, _, _, _) and - fwdFlowConsCand(t1, ap1, c, _, ap2) - ) - } - - pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret, - ParamNodeEx innerSummaryCtx, Typ innerArgT, Ap innerArgAp, ApApprox innerArgApa - ) { - fwdFlowThrough0(call, _, state, ccc, _, _, _, _, ap, apa, ret, innerSummaryCtx, innerArgT, - innerArgAp, innerArgApa) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, Ap ap - ) { - exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa | - returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argT, argAp, innerArgApa) and - flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap - ) { - exists(ApApprox argApa, Typ argT | - flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), - allowsFieldFlow, argApa) and - fwdFlow(arg, _, _, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - argApa) and - returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), ap) and - if allowsFieldFlow = false then argAp instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap - ) { - exists(ApApprox apa | - flowIntoCallApa(call, arg, p, allowsFieldFlow, apa) and - fwdFlow(arg, _, _, _, _, _, _, ap, apa) - ) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow, - Ap ap - ) { - exists(ApApprox apa | - flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa) and - fwdFlow(ret, _, _, _, _, _, _, ap, apa) and - pos = ret.getReturnPosition() - ) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, _, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, TReturnCtxNone(), returnAp, ap) and - flowIntoCallAp(_, node, p, allowsFieldFlow, ap) and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - returnCtx = TReturnCtxNone() - ) - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, node, p, _, ap, innerReturnAp) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) - ) - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Typ t, NodeEx node, FlowState state, NodeEx mid, - ReturnCtx returnCtx, ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, t, ap, c, mid, ap0) - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - ApOption returnAp, Ap ap - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), - pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, getApprox(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos, - ApOption returnAp, Ap ap, Ap innerReturnAp - ) { - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2 | - PrevStage::storeStepCand(node1, _, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, _, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _) - ) - } - - additional predicate revFlow(NodeEx node, FlowState state) { revFlow(node, state, _, _, _) } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - pragma[nomagic] - predicate revFlowAp(NodeEx node, Ap ap) { revFlow(node, _, _, _, ap) } - - private predicate fwdConsCand(Content c, Typ t, Ap ap) { storeStepFwd(_, t, ap, c, _, _) } - - private predicate revConsCand(Content c, Typ t, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, t, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Typ t, Ap tail | - consCand(head, t, tail) and - ap = apCons(head, t, tail) - ) - } - - additional predicate consCand(Content c, Typ t, Ap ap) { - revConsCand(c, t, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p, Ap innerReturnAp | - revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp) and - flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, - ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _)) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _, _)) and - conscand = count(Content f0, Typ t, Ap ap | consCand(f0, t, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) - } - /* End: Stage logic. */ - } - } - - private module BooleanCallContext { - class Cc extends boolean { - Cc() { this in [true, false] } - } - - class CcCall extends Cc { - CcCall() { this = true } - } - - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - } - - private module Level1CallContext { - class Cc = CallContext; - - class CcCall = CallContextCall; - - pragma[inline] - predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - module NoLocalCallContext { - class LocalCc = Unit; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - module LocalCallContext { - class LocalCc = LocalCallContext; - - bindingset[node, cc] - LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - bindingset[call, c, outercc] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() - } - } - - bindingset[call, c, innercc] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - } - - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; - - class Typ = Unit; - - class Ap extends boolean { - Ap() { this in [true, false] } - } - - class ApNil extends Ap { - ApNil() { this = false } - } - - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } - - Typ getTyp(DataFlowType t) { any() } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { - result = true and exists(c) and exists(t) and exists(tail) - } - - class ApHeadContent = Unit; - - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - - ApHeadContent projectToHeadContent(Content c) { any() } - - class ApOption = BooleanOption; - - ApOption apNone() { result = TBooleanNone() } - - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - - import Level1CallContext - import NoLocalCallContext - - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - ( - preservesValue = true and - localFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 - or - preservesValue = false and - additionalLocalStateStep(node1, state1, node2, state2) - ) and - exists(t) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - - predicate flowIntoCall = flowIntoCallNodeCand1/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { any() } - } - - private module Stage2 implements StageSig { - import MkStage::Stage - } - - pragma[nomagic] - private predicate flowOutOfCallNodeCand2( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - pragma[nomagic] - private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow) and - Stage2::revFlow(node2) and - Stage2::revFlow(node1) - } - - private module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - castNode(this.asNode()) or - clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) or - neverSkipInPathGraph(this.asNode()) or - Config::neverSkip(this.asNode()) - } - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state) { - Stage2::revFlow(node, state) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node) - or - additionalJumpStateStep(_, _, node, state) - or - node instanceof ParamNodeEx - or - node.asNode() instanceof OutNodeExt - or - Stage2::storeStepCand(_, _, _, node, _, _) - or - Stage2::readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state) { - exists(NodeEx next | Stage2::revFlow(next, state) | - jumpStepEx(node, next) or - additionalJumpStep(node, next) or - flowIntoCallNodeCand2(_, node, next, _) or - flowOutOfCallNodeCand2(_, node, _, next, _) or - Stage2::storeStepCand(node, _, _, next, _, _) or - Stage2::readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | Stage2::revFlow(next, s) | - additionalJumpStateStep(node, state, next, s) - or - additionalLocalStateStep(node, state, next, s) and - s != state - ) - or - Stage2::revFlow(node, state) and - node instanceof FlowCheckNode - or - sinkNode(node, state) - } - - pragma[noinline] - private predicate additionalLocalFlowStepNodeCand2( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2 - ) { - additionalLocalFlowStepNodeCand1(node1, node2) and - state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), false) and - Stage2::revFlow(node2, pragma[only_bind_into](state2), false) - or - additionalLocalStateStep(node1, state1, node2, state2) and - Stage2::revFlow(node1, state1, false) and - Stage2::revFlow(node2, state2, false) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc - ) { - not isUnreachableInCall1(node2, cc) and - ( - localFlowEntry(node1, pragma[only_bind_into](state)) and - ( - localFlowStepNodeCand1(node1, node2) and - preservesValue = true and - t = node1.getDataFlowType() and // irrelevant dummy value - Stage2::revFlow(node2, pragma[only_bind_into](state)) - or - additionalLocalFlowStepNodeCand2(node1, state, node2, state) and - preservesValue = false and - t = node2.getDataFlowType() - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, cc) - or - exists(NodeEx mid | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and - localFlowStepNodeCand1(mid, node2) and - not mid instanceof FlowCheckNode and - Stage2::revFlow(node2, pragma[only_bind_into](state)) - ) - or - exists(NodeEx mid | - localFlowStepPlus(node1, state, mid, _, _, cc) and - additionalLocalFlowStepNodeCand2(mid, state, node2, state) and - not mid instanceof FlowCheckNode and - preservesValue = false and - t = node2.getDataFlowType() - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext - ) { - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext) and - localFlowExit(node2, state1) and - state1 = state2 - or - additionalLocalFlowStepNodeCand2(node1, state1, node2, state2) and - state1 != state2 and - preservesValue = false and - t = node2.getDataFlowType() and - callContext.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, callContext) and - not isUnreachableInCall1(node2, callContext) - } - } - - private import LocalFlowBigStep - - pragma[nomagic] - private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } - - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; - - class Typ = DataFlowType; - - class Ap = ApproxAccessPathFront; - - class ApNil = ApproxAccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getAHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = ContentApprox; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - predicate projectToHeadContent = getContentApprox/1; - - class ApOption = ApproxAccessPathFrontOption; - - ApOption apNone() { result = TApproxAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } - - import BooleanCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - exists(lcc) - } - - predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - - predicate flowIntoCall = flowIntoCallNodeCand2/4; - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - // We can get away with not using type strengthening here, since we aren't - // going to use the tracked types in the construction of Stage 4 access - // paths. For Stage 4 and onwards, the tracked types must be consistent as - // the cons candidates including types are used to construct subsequent - // access path approximations. - t0 = t and - (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage3 implements StageSig { - import MkStage::Stage - } - - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if castingNodeEx(node) - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0) - ) - else t = t0 - } - - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; - - class Typ = DataFlowType; - - class Ap = AccessPathFront; - - class ApNil = AccessPathFrontNil; - - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.getHead() = c and exists(t) and exists(tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathFrontOption; - - ApOption apNone() { result = TAccessPathFrontNone() } - - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - - import BooleanCallContext - - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(lcc) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentCached(node.asNode(), c) - } - - pragma[nomagic] - private predicate clearContent(NodeEx node, Content c) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) - ) - } - - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { clearContent(node, ap.getHead()) } - - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - strengthenType(node, t0, t) and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - compatibleTypes(typ, contentType) - } - } - - private module Stage4 implements StageSig { - import MkStage::Stage - } - - /** - * Holds if `argApf` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - private predicate flowCandSummaryCtx(NodeEx node, FlowState state, AccessPathFront argApf) { - exists(AccessPathFront apf | - Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and - Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _, - apf, _) - ) - } - - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(DataFlowType t, AccessPathFront apf | Stage4::consCand(c, t, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } - - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c, DataFlowType t) { - Stage4::consCand(c, t, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, DataFlowType t, Content c2, int len) { - Stage4::consCand(c1, t, TFrontHead(c2)) and - len in [2 .. accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. accessPathLimit()] and - expensiveLen2unfolding(c) - } - - /** - * Conceptually a list of `Content`s where nested tails are also paired with a - * `DataFlowType`, but only the first two elements of the list and its length - * are tracked. If data flows from a source to a given node with a given - * `AccessPathApprox`, this indicates the sequence of dereference operations - * needed to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); - - abstract Content getHead(); - - abstract int len(); - - abstract AccessPathFront getFront(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail); - } - - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } - - override Content getHead() { none() } - - override int len() { result = 0 } - - override AccessPathFront getFront() { result = TFrontNil() } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { none() } - } - - abstract private class AccessPathApproxCons extends AccessPathApprox { } - - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; - private DataFlowType t; - - AccessPathApproxConsNil() { this = TConsNil(c, t) } - - override string toString() { - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + c.toString() + "]" + concat(" : " + ppReprType(t)) - } - - override Content getHead() { result = c } - - override int len() { result = 1 } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and typ = t and tail = TNil() - } - } - - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private DataFlowType t; - private Content c2; - private int len; - - AccessPathApproxConsCons() { this = TConsCons(c1, t, c2, len) } - - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c1 } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c1) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c1 and - typ = t and - ( - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - } - - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, DataFlowType typ, AccessPathApprox tail) { - head = c and - ( - exists(Content c2 | Stage4::consCand(c, typ, TFrontHead(c2)) | - tail = TConsCons(c2, _, _, len - 1) - or - len = 2 and - tail = TConsNil(c2, _) - or - tail = TCons1(c2, len - 1) - ) - or - len = 1 and - Stage4::consCand(c, typ, TFrontNil()) and - tail = TNil() - ) - } - } - - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) - - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } - } - - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; - - class Typ = DataFlowType; - - class Ap = AccessPathApprox; - - class ApNil = AccessPathApproxNil; - - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - - Typ getTyp(DataFlowType t) { result = t } - - bindingset[c, t, tail] - Ap apCons(Content c, Typ t, Ap tail) { result.isCons(c, t, tail) } - - class ApHeadContent = Content; - - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - - ApHeadContent projectToHeadContent(Content c) { result = c } - - class ApOption = AccessPathApproxOption; - - ApOption apNone() { result = TAccessPathApproxNone() } - - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - - import Level1CallContext - import LocalCallContext - - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) - } - - pragma[nomagic] - predicate flowOutOfCall( - DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - pragma[nomagic] - predicate flowIntoCall( - DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow - ) { - exists(FlowState state | - flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _) and - PrevStage::revFlow(node1, pragma[only_bind_into](state), _) - ) - } - - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) - } - - bindingset[typ, contentType] - predicate typecheckStore(Typ typ, DataFlowType contentType) { - compatibleTypes(typ, contentType) - } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate nodeMayUseSummary0( - NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa - ) { - exists(AccessPathApprox apa0 | - Stage5::parameterMayFlowThrough(p, _) and - Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and - Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _, - TAccessPathApproxSome(apa), _, apa0, _) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary(NodeEx n, FlowState state, AccessPathApprox apa) { - exists(ParamNodeEx p | - Stage5::parameterMayFlowThrough(p, apa) and - nodeMayUseSummary0(n, p, state, apa) - ) - } - - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) { - exists(AccessPathApprox apa | ap.getApprox() = apa | - Stage5::parameterMayFlowThrough(p, apa) and - Stage5::fwdFlow(p, state, _, _, Option::some(t), _, _, apa, _) and - Stage5::revFlow(p, state, _) - ) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - abstract private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState s; - private DataFlowType t; - private AccessPath ap; - - SummaryCtxSome() { this = TSummaryCtxSome(p, s, t, ap) } - - ParamNodeEx getParamNode() { result = p } - - override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = - strictcount(DataFlowType t, AccessPathFront apf | - Stage5::consCand(c, t, - any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - ) - ) - } - - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or nodeMayUseSummary(n, state, apa) - ) - } - - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } - - private predicate hasTail(AccessPathApprox apa, DataFlowType t, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, t, tail) and - Stage5::consCand(head, t, tail) - ) - } - - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, _, c2, _) and - forceHighPrecision(c2) - ) - } - - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) - } - - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = - strictsum(DataFlowType t, AccessPathApprox tail | hasTail(apa, t, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, DataFlowType t, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, t, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, DataFlowType t, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, t, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap - ) { - // A PathNode is introduced by a source ... - Stage5::revFlow(node, state) and - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - // ... or a step from an existing PathNode to another node. - pathStep(_, node, state, cc, sc, t, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.getNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSourceGroup(string sourceGroup) { - exists(PathNodeImpl source | sourceGroup = source.getSourceGroup()) - } or - TPathNodeSinkGroup(string sinkGroup) { - exists(PathNodeSink sink | sinkGroup = sink.getSinkGroup()) - } - - /** - * A list of `Content`s where nested tails are also paired with a - * `DataFlowType`. If data flows from a source to a given node with a given - * `AccessPath`, this indicates the sequence of dereference operations needed - * to get from the value in the node to the tracked object. The - * `DataFlowType`s indicate the types of the stored values. - */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); - - /** Holds if this is a representation of `head` followed by the `typ,tail` pair. */ - abstract predicate isCons(Content head, DataFlowType typ, AccessPath tail); - - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); - - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); - - /** Gets the length of this access path. */ - abstract int length(); - - /** Gets a textual representation of this access path. */ - abstract string toString(); - } - - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { none() } - - override AccessPathFrontNil getFront() { result = TFrontNil() } - - override AccessPathApproxNil getApprox() { result = TNil() } - - override int length() { result = 0 } - - override string toString() { result = "" } - } - - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private DataFlowType t; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, t, tail_) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and typ = t and tail = tail_ - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { - result = TConsNil(head_, t) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, t, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } - - override int length() { result = 1 + tail_.length() } - - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" + concat(" : " + ppReprType(t)) - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, _, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } - - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } - } - - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private DataFlowType t; - private Content head2; - private int len; - - AccessPathCons2() { this = TAccessPathCons2(head1, t, head2, len) } - - override Content getHead() { result = head1 } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head1 and - typ = t and - Stage5::consCand(head1, t, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, t, head2, len) or - result = TCons1(head1, len) - } - - override int length() { result = len } - - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" - } - } - - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; - private int len; - - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } - - override predicate isCons(Content head, DataFlowType typ, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, typ, tail.getApprox()) and - tail.length() = len - 1 - } - - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - - override int length() { result = len } - - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" - } - } - - abstract private class PathNodeImpl extends TPathNode { - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - abstract PathNodeImpl getASuccessorImpl(); - - private PathNodeImpl getASuccessorIfHidden() { - this.isHidden() and - result = this.getASuccessorImpl() - } - - pragma[nomagic] - private PathNodeImpl getANonHiddenSuccessor0() { - result = this.getASuccessorIfHidden*() and - not result.isHidden() - } - - final PathNodeImpl getANonHiddenSuccessor() { - result = this.getASuccessorImpl().getANonHiddenSuccessor0() and - not this.isHidden() - } - - abstract NodeEx getNodeEx(); - - predicate isHidden() { - not Config::includeHiddenNodes() and - ( - hiddenNode(this.getNodeEx().asNode()) and - not this.isSource() and - not this instanceof PathNodeSink - or - this.getNodeEx() instanceof TNodeImplicitRead - ) - } - - string getSourceGroup() { - this.isSource() and - Config::sourceGrouping(this.getNodeEx().asNode(), result) - } - - predicate isFlowSource() { - this.isSource() and not exists(this.getSourceGroup()) - or - this instanceof PathNodeSourceGroup - } - - predicate isFlowSink() { - this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or - this instanceof PathNodeSinkGroup - } - - private string ppType() { - this instanceof PathNodeSink and result = "" - or - exists(DataFlowType t | t = this.(PathNodeMid).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - this instanceof PathNodeSink and result = "" - or - exists(string s | s = this.(PathNodeMid).getAp().toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" - } - - private string ppSummaryCtx() { - this instanceof PathNodeSink and result = "" - or - result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" - } - - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + - this.ppSummaryCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - } - - /** Holds if `n` can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - n instanceof PathNodeSink or - n instanceof PathNodeSinkGroup or - directReach(n.getANonHiddenSuccessor()) - } - - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } - - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor() = n2 and directReach(n2) - } - - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) - - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { reach(this) } - - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { result = super.toStringWithContext() } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a grouping of source nodes. */ - final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group) } - - /** Holds if this node is a grouping of sink nodes. */ - final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group) } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } - - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - Subpaths::subpaths(arg, par, ret, out) - } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a `Node`, - * a `FlowState`, a `CallContext`, a `SummaryCtx`, and an `AccessPath`. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - CallContext cc; - SummaryCtx sc; - DataFlowType t; - AccessPath ap; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, t, ap) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - SummaryCtx getSummaryCtx() { result = sc } - - DataFlowType getType() { result = t } - - AccessPath getAp() { result = ap } - - private PathNodeMid getSuccMid() { - pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx(), result.getType(), result.getAp()) - } - - override PathNodeImpl getASuccessorImpl() { - // an intermediate step to another intermediate node - result = this.getSuccMid() - or - // a final step to a sink - result = this.getSuccMid().projectToSink() - } - - override predicate isSource() { - sourceNode(node, state) and - sourceCallCtx(cc) and - sc instanceof SummaryCtxNone and - t = node.getDataFlowType() and - ap = TAccessPathNil() - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof AccessPathNil and - if hasSinkCallCtx() - then - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. This also implies - // `sc instanceof SummaryCtxNone`. - // For `FeatureEqualSourceSinkCallContext` the initial call context was - // set to `CallContextSomeCall` and jumps are disallowed, so - // `cc instanceof CallContextNoCall` never holds. On the other hand, - // in this case there's never any need to enter a call except to identify - // a summary, so the condition in `pathIntoCallable` enforces this, which - // means that `sc instanceof SummaryCtxNone` holds if and only if we are - // in the call context of the source. - sc instanceof SummaryCtxNone or - cc instanceof CallContextNoCall - else any() - } - - PathNodeSink projectToSink() { - this.isAtSink() and - result.getNodeEx() = node and - result.getState() = state - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the `CallContext`. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override PathNodeImpl getASuccessorImpl() { result = TPathNodeSinkGroup(this.getSinkGroup()) } - - override predicate isSource() { sourceNode(node, state) } - - string getSinkGroup() { Config::sinkGrouping(node.asNode(), result) } - } - - private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup { - string sourceGroup; - - PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { result.getSourceGroup() = sourceGroup } - - override predicate isSource() { none() } - - override string toString() { result = sourceGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup { - string sinkGroup; - - PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl() { none() } - - override predicate isSource() { none() } - - override string toString() { result = sinkGroup } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 - } - } - - private predicate pathNode( - PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap, LocalCallContext localCC - ) { - midnode = mid.getNodeEx() and - state = mid.getState() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - midnode.getEnclosingCallable()) and - t = mid.getType() and - ap = mid.getAp() - } - - private predicate pathStep( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(DataFlowType t0 | - pathStep0(mid, node, state, cc, sc, t0, ap) and - Stage5::revFlow(node, state, ap.getApprox()) and - strengthenType(node, t0, t) - ) - } - - /** - * Holds if data may flow from `mid` to `node`. The last step in or out of - * a callable is recorded by `cc`. - */ - pragma[nomagic] - private predicate pathStep0( - PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, - AccessPath ap - ) { - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, t, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, true, _, localCC) - ) - or - exists(NodeEx midnode, FlowState state0, LocalCallContext localCC | - pathNode(mid, midnode, state0, cc, sc, _, ap, localCC) and - localFlowBigStep(midnode, state0, node, state, false, t, localCC) and - ap instanceof AccessPathNil - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - t = node.getDataFlowType() and - ap = TAccessPathNil() - or - exists(Content c, DataFlowType t0, AccessPath ap0 | - pathStoreStep(mid, node, state, t0, ap0, c, t, cc) and - ap.isCons(c, t0, ap0) and - sc = mid.getSummaryCtx() - ) - or - exists(Content c, AccessPath ap0 | - pathReadStep(mid, node, state, ap0, c, cc) and - ap0.isCons(c, t, ap) and - sc = mid.getSummaryCtx() - ) - or - pathIntoCallable(mid, node, state, _, cc, sc, _) and t = mid.getType() and ap = mid.getAp() - or - pathOutOfCallable(mid, node, state, cc) and - t = mid.getType() and - ap = mid.getAp() and - sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, state, cc, t, ap) and sc = mid.getSummaryCtx() - } - - pragma[nomagic] - private predicate pathReadStep( - PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, Content c, CallContext cc - ) { - ap0 = mid.getAp() and - c = ap0.getHead() and - Stage5::readStepCand(mid.getNodeEx(), c, node) and - state = mid.getState() and - cc = mid.getCallContext() - } - - pragma[nomagic] - private predicate pathStoreStep( - PathNodeMid mid, NodeEx node, FlowState state, DataFlowType t0, AccessPath ap0, Content c, - DataFlowType t, CallContext cc - ) { - exists(DataFlowType contentType | - t0 = mid.getType() and - ap0 = mid.getAp() and - Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and - state = mid.getState() and - cc = mid.getCallContext() and - compatibleTypes(t0, contentType) - ) - } - - private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - apa = mid.getAp().getApprox() - } - - pragma[nomagic] - private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, - AccessPathApprox apa - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, state, innercc, apa) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - pragma[noinline] - private NodeEx getAnOutNodeFlow(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa) { - result.asNode() = kind.getAnOutNode(call) and - Stage5::revFlow(result, _, apa) - } - - /** - * Holds if data may flow from `mid` to `out`. The last step of this path - * is a return from a callable and is recorded by `cc`, if needed. - */ - pragma[noinline] - private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa | - pathOutOfCallable1(mid, call, kind, state, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - /** - * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. - */ - pragma[noinline] - private predicate pathIntoArg( - PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(ArgNodeEx arg, ArgumentPosition apos | - pathNode(mid, arg, state, cc, _, t, ap, _) and - arg.asNode().(ArgNode).argumentOf(call, apos) and - apa = ap.getApprox() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate parameterCand( - DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa - ) { - exists(ParamNodeEx p | - Stage5::revFlow(p, _, apa) and - p.isParameterOf(callable, pos) - ) - } - - pragma[nomagic] - private predicate pathIntoCallable0( - PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, AccessPath ap - ) { - exists(AccessPathApprox apa | - pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, t, ap, - pragma[only_bind_into](apa)) and - callable = resolveCall(call, outercc) and - parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa)) - ) - } - - /** - * Holds if data may flow from `mid` to `p` through `call`. The contexts - * before and after entering the callable are `outercc` and `innercc`, - * respectively. - */ - pragma[nomagic] - private predicate pathIntoCallable( - PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, - SummaryCtx sc, DataFlowCall call - ) { - exists(ParameterPosition pos, DataFlowCallable callable, DataFlowType t, AccessPath ap | - pathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - ( - sc = TSummaryCtxSome(p, state, t, ap) - or - not exists(TSummaryCtxSome(p, state, t, ap)) and - sc = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ - pragma[nomagic] - private predicate paramFlowsThrough( - ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, DataFlowType t, - AccessPath ap, AccessPathApprox apa - ) { - exists(RetNodeEx ret | - pathNode(_, ret, state, cc, sc, t, ap, _) and - kind = ret.getKind() and - apa = ap.getApprox() and - parameterFlowThroughAllowed(sc.getParamNode(), kind) - ) - } - - pragma[nomagic] - private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, - DataFlowType t, AccessPath ap, AccessPathApprox apa - ) { - exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, state, innercc, sc, t, ap, apa) - ) - } - - /** - * Holds if data may flow from `mid` through a callable to the node `out`. - * The context `cc` is restored to its value prior to entering the callable. - */ - pragma[noinline] - private predicate pathThroughCallable( - PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, AccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, state, cc, t, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa) - ) - } - - private module Subpaths { - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths01( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](t), - pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, _, innercc, sc, _) and - paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, pragma[only_bind_into](t), - pragma[only_bind_into](apout), _) and - not arg.isHidden() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by - * `kind`, `sc`, `sout`, `apout`, and `innercc`. - */ - pragma[nomagic] - private predicate subpaths02( - PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, - NodeEx out, FlowState sout, DataFlowType t, AccessPath apout - ) { - subpaths01(arg, par, sc, innercc, kind, out, sout, t, apout) and - out.asNode() = kind.getAnOutNode(_) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - */ - pragma[nomagic] - private predicate subpaths03( - PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, - DataFlowType t, AccessPath apout - ) { - exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | - subpaths02(arg, par, sc, innercc, kind, out, sout, t, apout) and - pathNode(ret, retnode, sout, innercc, sc, t, apout, _) and - kind = retnode.getKind() - ) - } - - private PathNodeImpl localStepToHidden(PathNodeImpl n) { - n.getASuccessorImpl() = result and - result.isHidden() and - exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | - localFlowBigStep(n1, _, n2, _, _, _, _) or - storeEx(n1, _, n2, _, _) or - readSetEx(n1, _, n2) - ) - } - - pragma[nomagic] - private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { - succ = pred.getANonHiddenSuccessor() and - succNode = succ.getNodeEx() - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) { - exists( - ParamNodeEx p, NodeEx o, FlowState sout, DataFlowType t, AccessPath apout, PathNodeMid out0 - | - pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and - subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, t, apout) and - hasSuccessor(pragma[only_bind_into](arg), par, p) and - not ret.isHidden() and - pathNode(out0, o, sout, _, _, t, apout, _) - | - out = out0 or out = out0.projectToSink() - ) - } - - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - predicate retReach(PathNodeImpl n) { - exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor() = mid and - not subpaths(_, mid, _, _) - ) - } - } - - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isFlowSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isFlowSink() - ) - } - - /** DEPRECATED: Use `flowPath` instead. */ - deprecated predicate hasFlowPath = flowPath/2; - - private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) { - flowsource.isSource() and - flowsource.getNodeEx().asNode() = source and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.getNodeEx().asNode() = sink - } - - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) } - - /** DEPRECATED: Use `flow` instead. */ - deprecated predicate hasFlow = flow/2; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() } - - /** DEPRECATED: Use `flowTo` instead. */ - deprecated predicate hasFlowTo = flowTo/1; - - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - - /** DEPRECATED: Use `flowToExpr` instead. */ - deprecated predicate hasFlowToExpr = flowToExpr/1; - - private predicate finalStats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples - ) { - fwd = true and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and - tuples = count(PathNodeImpl pn) - or - fwd = false and - nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and - fields = count(Content f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and - conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and - states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and - tuples = count(PathNode pn) - } - - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples) - or - stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples) - or - stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples) - } - - module FlowExploration { - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2) - or - additionalJumpStateStep(node1, _, node2, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } - - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | Config::isSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } - - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | Config::isSink(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() - - private predicate callableExtSrc(TCallableSrc src) { any() } - - private predicate callableExtSink(TCallableSink sink) { any() } - - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) - ) - or - exists(Node n | - ce1 = TCallableSrc() and - Config::isSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) - ) - or - exists(Node n | - ce2 = TCallableSink() and - Config::isSink(n, _) and - ce1 = TCallable(getNodeEnclosingCallable(n)) - ) - } - - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) - - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) - - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } - - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } - - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } - - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); - - Content getHead() { this = TPartialCons(result, _) } - - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } - - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } - - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } - - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNode(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) - } - - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) - - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) - - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) - - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) - - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } - - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) - - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, - PartialAccessPath ap - ) { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } - - pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) - } - - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not clearsContentEx(node, ap.getHead()) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - } - - pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } - - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } - - FlowState getState() { none() } - - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } - - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } - - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } - - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } - - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(DataFlowType t | t = this.(PartialPathNodeFwd).getType() | - // The `concat` becomes "" if `ppReprType` has no result. - result = concat(" : " + ppReprType(t)) - ) - } - - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } - - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } - - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } - - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - CallContext getCallContext() { result = cc } - - TSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TSummaryCtx3 getSummaryCtx3() { result = sc3 } - - TSummaryCtx4 getSummaryCtx4() { result = sc4 } - - DataFlowType getType() { result = t } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeFwd getASuccessor() { - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } - - predicate isSource() { - sourceNode(node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } - } - - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } - - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } - - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } - - PartialAccessPath getAp() { result = ap } - - override PartialPathNodeRev getASuccessor() { - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } - - predicate isSink() { - sinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } - - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and - ( - localFlowStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() - or - additionalJumpStep(mid.getNodeEx(), node) and - state = mid.getState() and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and - cc instanceof CallContextAny and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() - } - - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } - - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeEx(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(t1, contentType) - ) - } - - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } - - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } - - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } - - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - resolveReturn(innercc, c, call) - | - if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() - ) - } - - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) - | - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = resolveCall(call, outercc) - } - - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) - } - - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } - - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } - - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out.asNode() = kind.getAnOutNode(call) - ) - } - - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - localFlowStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() - or - additionalLocalFlowStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - or - additionalJumpStep(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() - ) - or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = getReturnPosition(node.asNode()) - ) - or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeEx(node, c, midNode, _, _) and - ap.getHead() = c - ) - } - - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap - ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) - } - - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap - ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap - ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) - } - - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap - ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.asNode().(ArgNode).argumentOf(call, pos) - ) - } - - private predicate partialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) { - partialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() - } - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImpl +import MakeImpl diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll index b0de9745816..1975ac9781f 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll @@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig { getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty } + predicate isSink(Node sink) { none() } + predicate isSink(Node sink, FlowState state) { getConfig(state).isSink(sink, getState(state)) or diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll index aff14e7b44d..969275ffa07 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll @@ -1,1481 +1,3 @@ -private import DataFlowImplSpecific::Private -private import DataFlowImplSpecific::Public -import Cached - -module DataFlowImplCommonPublic { - /** Provides `FlowState = string`. */ - module FlowStateString { - /** A state value to track during data flow. */ - class FlowState = string; - - /** - * The default state, which is used when the state is unspecified for a source - * or a sink. - */ - class FlowStateEmpty extends FlowState { - FlowStateEmpty() { this = "" } - } - } - - private newtype TFlowFeature = - TFeatureHasSourceCallContext() or - TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() - - /** A flow configuration feature for use in `Configuration::getAFeature()`. */ - class FlowFeature extends TFlowFeature { - string toString() { none() } - } - - /** - * A flow configuration feature that implies that sources have some existing - * call context. - */ - class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { - override string toString() { result = "FeatureHasSourceCallContext" } - } - - /** - * A flow configuration feature that implies that sinks have some existing - * call context. - */ - class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { - override string toString() { result = "FeatureHasSinkCallContext" } - } - - /** - * A flow configuration feature that implies that source-sink pairs have some - * shared existing call context. - */ - class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { - override string toString() { result = "FeatureEqualSourceSinkCallContext" } - } -} - -/** - * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision during pruning. - */ -predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { - apLimit = 10 and - tupleLimit = 10000 -} - -/** - * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. - * - * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the - * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of - * these limits are represented with lower precision. - */ -predicate accessPathCostLimits(int apLimit, int tupleLimit) { - apLimit = 5 and - tupleLimit = 1000 -} - -/** - * Holds if `arg` is an argument of `call` with an argument position that matches - * parameter position `ppos`. - */ -pragma[noinline] -predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { - exists(ArgumentPosition apos | - arg.argumentOf(call, apos) and - parameterMatch(ppos, apos) - ) -} - -/** - * Provides a simple data-flow analysis for resolving lambda calls. The analysis - * currently excludes read-steps, store-steps, and flow-through. - * - * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lambda - * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. - */ -private module LambdaFlow { - pragma[noinline] - private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallable(call), ppos) - } - - pragma[noinline] - private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableLambda(call, _), ppos) - } - - private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamNonLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParamLambda(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) - ) - } - - private newtype TReturnPositionSimple = - TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { - exists(ReturnNode ret | - c = getNodeEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - pragma[noinline] - private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { - result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallable(call), kind) - } - - pragma[nomagic] - private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) { - result = TReturnPositionSimple0(viableCallableLambda(call, _), kind) - } - - private predicate viableReturnPosOutNonLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosNonLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - pragma[nomagic] - private predicate viableReturnPosOutLambda( - DataFlowCall call, TReturnPositionSimple pos, OutNode out - ) { - exists(ReturnKind kind | - pos = viableReturnPosLambda(call, kind) and - out = getAnOutNode(call, kind) - ) - } - - /** - * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to - * the lambda call `lambdaCall`. - * - * The parameter `toReturn` indicates whether the path from `node` to - * `lambdaCall` goes through a return, and `toJump` whether the path goes - * through a jump step. - * - * The call context `lastCall` records the last call on the path from `node` - * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing - * callable of `lambdaCall`. - */ - pragma[nomagic] - predicate revLambdaFlow( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - not expectsContent(node, _) and - if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - predicate revLambdaFlow0( - DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, - boolean toJump, DataFlowCallOption lastCall - ) { - lambdaCall(lambdaCall, kind, node) and - t = getNodeDataFlowType(node) and - toReturn = false and - toJump = false and - lastCall = TDataFlowCallNone() - or - // local flow - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) - | - simpleLocalFlowStep(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // jump step - exists(Node mid, DataFlowType t0 | - revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and - toReturn = false and - toJump = true - | - jumpStepCached(node, mid) and - t = t0 - or - exists(boolean preservesValue | - additionalLambdaFlowStep(node, mid, preservesValue) and - getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) - | - preservesValue = false and - t = getNodeDataFlowType(node) - or - preservesValue = true and - t = t0 - ) - ) - or - // flow into a callable - exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | - revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and - ( - if lastCall0 = TDataFlowCallNone() and toJump = false - then lastCall = TDataFlowCallSome(call) - else lastCall = lastCall0 - ) and - toReturn = false - | - viableParamArgNonLambda(call, p, node) - or - viableParamArgLambda(call, p, node) // non-linear recursion - ) - or - // flow out of a callable - exists(TReturnPositionSimple pos | - revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and - getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and - toReturn = true - ) - } - - pragma[nomagic] - predicate revLambdaFlowOutLambdaCall( - DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, - DataFlowCall call, DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - exists(ReturnKindExt rk | - out = rk.getAnOutNode(call) and - lambdaCall(call, _, _) - ) - } - - pragma[nomagic] - predicate revLambdaFlowOut( - DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall - ) { - exists(DataFlowCall call, OutNode out | - revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and - viableReturnPosOutNonLambda(call, pos, out) - or - // non-linear recursion - revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and - viableReturnPosOutLambda(call, pos, out) - ) - } - - pragma[nomagic] - predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall - ) { - revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) - } -} - -private DataFlowCallable viableCallableExt(DataFlowCall call) { - result = viableCallable(call) - or - result = viableCallableLambda(call, _) -} - -cached -private module Cached { - /** - * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to - * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } - - cached - predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { - c = call.getEnclosingCallable() - } - - cached - predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } - - cached - predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } - - cached - predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } - - cached - predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } - - cached - predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } - - cached - predicate outNodeExt(Node n) { - n instanceof OutNode - or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode - } - - cached - predicate hiddenNode(Node n) { nodeIsHidden(n) } - - cached - OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { - result = getAnOutNode(call, k.(ValueReturnKind).getKind()) - or - exists(ArgNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) - ) - } - - cached - predicate returnNodeExt(Node n, ReturnKindExt k) { - k = TValueReturn(n.(ReturnNode).getKind()) - or - exists(ParamNode p, ParameterPosition pos | - parameterValueFlowsToPreUpdate(p, n) and - p.isParameterOf(_, pos) and - k = TParamUpdate(pos) - ) - } - - cached - predicate castNode(Node n) { n instanceof CastNode } - - cached - predicate castingNode(Node n) { - castNode(n) or - n instanceof ParamNode or - n instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readSet(_, _, n) - } - - cached - predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { - isParameterNode(p, c, pos) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { - isArgumentNode(n, call, pos) - } - - /** - * Gets a viable target for the lambda call `call`. - * - * `lastCall` records the call required to reach `call` in order for the result - * to be a viable target, if any. - */ - cached - DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { - exists(Node creation, LambdaCallKind kind | - LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and - lambdaCreation(creation, kind, result) - ) - } - - /** - * Holds if `p` is the parameter of a viable dispatch target of `call`, - * and `p` has position `ppos`. - */ - pragma[nomagic] - private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { - p.isParameterOf(viableCallableExt(call), ppos) - } - - /** - * Holds if `arg` is a possible argument to `p` in `call`, taking virtual - * dispatch into account. - */ - cached - predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { - exists(ParameterPosition ppos | - viableParam(call, ppos, p) and - argumentPositionMatch(call, arg, ppos) and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and - golangSpecificParamArgFilter(call, p, arg) - ) - } - - pragma[nomagic] - private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallableExt(call) = result.getCallable() and - kind = result.getKind() - } - - /** - * Holds if a value at return position `pos` can be returned to `out` via `call`, - * taking virtual dispatch into account. - */ - cached - predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { - exists(ReturnKindExt kind | - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) - } - - /** Provides predicates for calculating flow-through summaries. */ - private module FlowThrough { - /** - * The first flow-through approximation: - * - * - Input access paths are abstracted with a Boolean parameter - * that indicates (non-)emptiness. - */ - private module Cand { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps. - * - * `read` indicates whether it is contents of `p` that can flow to `node`. - */ - pragma[nomagic] - private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { - p = node and - read = false - or - // local flow - exists(Node mid | - parameterValueFlowCand(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlowCand(p, mid, false) and - readSet(mid, _, node) and - read = true - ) - or - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, false) and - argumentValueFlowsThroughCand(arg, node, read) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArgCand(p, arg, read) and - argumentValueFlowsThroughCand(arg, node, false) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { - parameterValueFlowCand(p, arg, read) - } - - pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps, not taking call contexts - * into account. - * - * `read` indicates whether it is contents of `p` that can flow to the return - * node. - */ - predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { - exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read) and - kind = ret.getKind() - ) - } - - pragma[nomagic] - private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. - * - * `read` indicates whether it is contents of `arg` that can flow to `out`. - */ - predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThroughCand0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - ) - } - - predicate cand(ParamNode p, Node n) { - parameterValueFlowCand(p, n, _) and - ( - parameterValueFlowReturnCand(p, _, _) - or - parameterValueFlowsToPreUpdateCand(p, _) - ) - } - } - - /** - * The final flow-through calculation: - * - * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) - * or summarized as a single read step with before and after types recorded - * in the `ReadStepTypesOption` parameter. - * - Types are checked using the `compatibleTypes()` relation. - */ - private module Final { - /** - * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { - parameterValueFlow0(p, node, read) and - if node instanceof CastingNode - then - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) - or - // getter - compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) - else any() - } - - pragma[nomagic] - private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { - p = node and - Cand::cand(p, _) and - read = TReadStepTypesNone() - or - // local flow - exists(Node mid | - parameterValueFlow(p, mid, read) and - simpleLocalFlowStep(mid, node) - ) - or - // read - exists(Node mid | - parameterValueFlow(p, mid, TReadStepTypesNone()) and - readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, - read.getContentType()) and - Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) - ) - or - parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) - } - - pragma[nomagic] - private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read - ) { - // flow through: no prior read - exists(ArgNode arg | - parameterValueFlowArg(p, arg, mustBeNone) and - argumentValueFlowsThrough(arg, read, node) - ) - or - // flow through: no read inside method - exists(ArgNode arg | - parameterValueFlowArg(p, arg, read) and - argumentValueFlowsThrough(arg, mustBeNone, node) - ) - } - - pragma[nomagic] - private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { - parameterValueFlow(p, arg, read) and - Cand::argumentValueFlowsThroughCand(arg, _, _) - } - - pragma[nomagic] - private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ParamNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, read) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and possibly a single read step, not taking - * call contexts into account. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - pragma[nomagic] - predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { - exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, read) and - out = getAnOutNode(call, kind) - | - // normal flow through - read = TReadStepTypesNone() and - compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) - or - // getter - compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) - ) - } - - /** - * Holds if `arg` flows to `out` through a call using only - * value-preserving steps and a single read step, not taking call - * contexts into account, thus representing a getter-step. - * - * This predicate is exposed for testing only. - */ - predicate getterStep(ArgNode arg, ContentSet c, Node out) { - argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) - } - - /** - * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps and possibly a single read - * step. - * - * If a read step was taken, then `read` captures the `Content`, the - * container type, and the content type. - */ - private predicate parameterValueFlowReturn( - ParamNode p, ReturnKind kind, ReadStepTypesOption read - ) { - exists(ReturnNode ret | - parameterValueFlow(p, ret, read) and - kind = ret.getKind() - ) - } - } - - import Final - } - - import FlowThrough - - cached - private module DispatchWithCallContext { - /** - * Holds if the set of viable implementations that can be called by `call` - * might be improved by knowing the call context. - */ - pragma[nomagic] - private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { - mayBenefitFromCallContext(call, callable) - or - callEnclosingCallable(call, callable) and - exists(viableCallableLambda(call, TDataFlowCallSome(_))) - } - - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference. - */ - cached - DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - result = viableCallable(call) - or - result = viableCallableLambda(call, TDataFlowCallSome(ctx)) - or - exists(DataFlowCallable enclosing | - mayBenefitFromCallContextExt(call, enclosing) and - enclosing = viableCallableExt(ctx) and - result = viableCallableLambda(call, TDataFlowCallNone()) - ) - } - - /** - * Holds if the call context `ctx` reduces the set of viable run-time - * dispatch targets of call `call` in `c`. - */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, c) and - c = viableCallableExt(ctx) and - ctxtgts = count(viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(viableCallableExt(call)) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls for which a context - * makes a difference. - */ - cached - DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. - */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - mayBenefitFromCallContextExt(call, _) and - c = viableCallableExt(call) and - ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and - ctxtgts < tgts - ) - } - - /** - * Gets a viable run-time dispatch target for the call `call` in the - * context `ctx`. This is restricted to those calls and results for which - * the return flow from the result to `call` restricts the possible context - * `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContextExt(call, ctx) and - reducedViableImplInReturn(result, call) - } - } - - import DispatchWithCallContext - - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) - } - - cached - predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } - - cached - predicate storeSet( - Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - storeStep(node1, c, node2) and - contentType = getNodeDataFlowType(node1) and - containerType = getNodeDataFlowType(node2) - or - exists(Node n1, Node n2 | - n1 = node1.(PostUpdateNode).getPreUpdateNode() and - n2 = node2.(PostUpdateNode).getPreUpdateNode() - | - argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) - or - readSet(n2, c, n1) and - contentType = getNodeDataFlowType(n1) and - containerType = getNodeDataFlowType(n2) - ) - } - - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `c`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store( - Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType - ) { - exists(ContentSet cs | - c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) - ) - } - - /** - * Holds if data can flow from `fromNode` to `toNode` because they are the post-update - * nodes of some function output and input respectively, where the output and input - * are aliases. A typical example is a function returning `this`, implementing a fluent - * interface. - */ - private predicate reverseStepThroughInputOutputAlias( - PostUpdateNode fromNode, PostUpdateNode toNode - ) { - exists(Node fromPre, Node toPre | - fromPre = fromNode.getPreUpdateNode() and - toPre = toNode.getPreUpdateNode() - | - exists(DataFlowCall c | - // Does the language-specific simpleLocalFlowStep already model flow - // from function input to output? - fromPre = getAnOutNode(c, _) and - toPre.(ArgNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgNode), fromPre) - ) - or - argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) - ) - } - - cached - predicate simpleLocalFlowStepExt(Node node1, Node node2) { - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - } - - /** - * Holds if the call context `call` improves virtual dispatch in `callable`. - */ - cached - predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { - reducedViableImplInCallContext(_, callable, call) - } - - /** - * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. - */ - cached - predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) - } - - cached - predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } - - cached - newtype TCallContext = - TAnyCallContext() or - TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or - TSomeCall() or - TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } - - cached - newtype TReturnPosition = - TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { - exists(ReturnNodeExt ret | - c = returnNodeGetEnclosingCallable(ret) and - kind = ret.getKind() - ) - } - - cached - newtype TLocalFlowCallContext = - TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } - - cached - newtype TReturnKindExt = - TValueReturn(ReturnKind kind) or - TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } - - cached - newtype TBooleanOption = - TBooleanNone() or - TBooleanSome(boolean b) { b = true or b = false } - - cached - newtype TDataFlowCallOption = - TDataFlowCallNone() or - TDataFlowCallSome(DataFlowCall call) - - cached - newtype TParamNodeOption = - TParamNodeNone() or - TParamNodeSome(ParamNode p) - - cached - newtype TReturnCtx = - TReturnCtxNone() or - TReturnCtxNoFlowThrough() or - TReturnCtxMaybeFlowThrough(ReturnPosition pos) - - cached - newtype TAccessPathFront = - TFrontNil() or - TFrontHead(Content c) - - cached - newtype TApproxAccessPathFront = - TApproxFrontNil() or - TApproxFrontHead(ContentApprox c) - - cached - newtype TAccessPathFrontOption = - TAccessPathFrontNone() or - TAccessPathFrontSome(AccessPathFront apf) - - cached - newtype TApproxAccessPathFrontOption = - TApproxAccessPathFrontNone() or - TApproxAccessPathFrontSome(ApproxAccessPathFront apf) -} - -/** - * Holds if the call context `call` either improves virtual dispatch in - * `callable` or if it allows us to prune unreachable nodes in `callable`. - */ -predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { - recordDataFlowCallSiteDispatch(call, callable) or - recordDataFlowCallSiteUnreachable(call, callable) -} - -/** - * A `Node` at which a cast can occur such that the type should be checked. - */ -class CastingNode instanceof Node { - CastingNode() { castingNode(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private predicate readStepWithTypes( - Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content -) { - readSet(n1, c, n2) and - container = getNodeDataFlowType(n1) and - content = getNodeDataFlowType(n2) -} - -private newtype TReadStepTypesOption = - TReadStepTypesNone() or - TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { - readStepWithTypes(_, container, c, _, content) - } - -private class ReadStepTypesOption extends TReadStepTypesOption { - predicate isSome() { this instanceof TReadStepTypesSome } - - DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } - - ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } - - DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } - - string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } -} - -/** - * A call context to restrict the targets of virtual dispatch, prune local flow, - * and match the call sites of flow into a method with flow out of a method. - * - * There are four cases: - * - `TAnyCallContext()` : No restrictions on method flow. - * - `TSpecificCall(DataFlowCall call)` : Flow entered through the - * given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable - * or helps prune unreachable nodes in the current callable. - * - `TSomeCall()` : Flow entered through a parameter. The - * originating call does not improve the set of dispatch targets for any - * method call in the current callable and was therefore not recorded. - * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and - * this dispatch target of `call` implies a reduced set of dispatch origins - * to which data may flow if it should reach a `return` statement. - */ -abstract class CallContext extends TCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -abstract class CallContextNoCall extends CallContext { } - -class CallContextAny extends CallContextNoCall, TAnyCallContext { - override string toString() { result = "CcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -abstract class CallContextCall extends CallContext { - /** Holds if this call context may be `call`. */ - bindingset[call] - abstract predicate matchesCall(DataFlowCall call); -} - -class CallContextSpecificCall extends CallContextCall, TSpecificCall { - override string toString() { - exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(this.getCall(), callable) - } - - override predicate matchesCall(DataFlowCall call) { call = this.getCall() } - - DataFlowCall getCall() { this = TSpecificCall(result) } -} - -class CallContextSomeCall extends CallContextCall, TSomeCall { - override string toString() { result = "CcSomeCall" } - - override predicate relevantFor(DataFlowCallable callable) { - exists(ParamNode p | getNodeEnclosingCallable(p) = callable) - } - - override predicate matchesCall(DataFlowCall call) { any() } -} - -class CallContextReturn extends CallContextNoCall, TReturn { - override string toString() { - exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") - } - - override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) - } -} - -/** - * A call context that is relevant for pruning local flow. - */ -abstract class LocalCallContext extends TLocalFlowCallContext { - abstract string toString(); - - /** Holds if this call context is relevant for `callable`. */ - abstract predicate relevantFor(DataFlowCallable callable); -} - -class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { - override string toString() { result = "LocalCcAny" } - - override predicate relevantFor(DataFlowCallable callable) { any() } -} - -class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { - LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } - - DataFlowCall call; - - DataFlowCall getCall() { result = call } - - override string toString() { result = "LocalCcCall(" + call + ")" } - - override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } -} - -private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) -} - -/** - * Gets the local call context given the call context and the callable that - * the contexts apply to. - */ -LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { - ctx.relevantFor(callable) and - if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) - then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() - else result instanceof LocalCallContextAny -} - -/** - * The value of a parameter at function entry, viewed as a node in a data - * flow graph. - */ -class ParamNode instanceof Node { - ParamNode() { parameterNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * Holds if this node is the parameter of callable `c` at the specified - * position. - */ - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } -} - -/** A data-flow node that represents a call argument. */ -class ArgNode instanceof Node { - ArgNode() { argumentNode(this, _, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - argumentNode(this, call, pos) - } -} - -/** - * A node from which flow can return to the caller. This is either a regular - * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. - */ -class ReturnNodeExt instanceof Node { - ReturnNodeExt() { returnNodeExt(this, _) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { returnNodeExt(this, result) } -} - -/** - * A node to which data can flow from a call. Either an ordinary out node - * or a post-update node associated with a call argument. - */ -class OutNodeExt instanceof Node { - OutNodeExt() { outNodeExt(this) } - - string toString() { result = super.toString() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -/** - * An extended return kind. A return kind describes how data can be returned - * from a callable. This can either be through a returned value or an updated - * parameter. - */ -abstract class ReturnKindExt extends TReturnKindExt { - /** Gets a textual representation of this return kind. */ - abstract string toString(); - - /** Gets a node corresponding to data flow out of `call`. */ - final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } -} - -class ValueReturnKind extends ReturnKindExt, TValueReturn { - private ReturnKind kind; - - ValueReturnKind() { this = TValueReturn(kind) } - - ReturnKind getKind() { result = kind } - - override string toString() { result = kind.toString() } -} - -class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { - private ParameterPosition pos; - - ParamUpdateReturnKind() { this = TParamUpdate(pos) } - - ParameterPosition getPosition() { result = pos } - - pragma[nomagic] - ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } - - override string toString() { result = "param update " + pos } -} - -/** A callable tagged with a relevant return kind. */ -class ReturnPosition extends TReturnPosition0 { - private DataFlowCallable c; - private ReturnKindExt kind; - - ReturnPosition() { this = TReturnPosition0(c, kind) } - - /** Gets the callable. */ - DataFlowCallable getCallable() { result = c } - - /** Gets the return kind. */ - ReturnKindExt getKind() { result = kind } - - /** Gets a textual representation of this return position. */ - string toString() { result = "[" + kind + "] " + c } -} - -/** - * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this - * predicate ensures that joins go from `n` to the result instead of the other - * way around. - */ -pragma[inline] -DataFlowCallable getNodeEnclosingCallable(Node n) { - nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -/** Gets the type of `n` used for type pruning. */ -pragma[inline] -DataFlowType getNodeDataFlowType(Node n) { - nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) -} - -pragma[noinline] -private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { - result = getNodeEnclosingCallable(ret) -} - -pragma[noinline] -private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { - result.getCallable() = returnNodeGetEnclosingCallable(ret) and - kind = result.getKind() -} - -pragma[noinline] -ReturnPosition getReturnPosition(ReturnNodeExt ret) { - result = getReturnPosition0(ret, ret.getKind()) -} - -/** - * Checks whether `inner` can return to `call` in the call context `innercc`. - * Assumes a context of `inner = viableCallableExt(call)`. - */ -bindingset[innercc, inner, call] -predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { - innercc instanceof CallContextAny - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, inner) and - innercc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Checks whether `call` can resolve to `calltarget` in the call context `cc`. - * Assumes a context of `calltarget = viableCallableExt(call)`. - */ -bindingset[cc, call, calltarget] -predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then calltarget = prunedViableImplInCallContext(call, ctx) - else any() - ) - or - cc instanceof CallContextSomeCall - or - cc instanceof CallContextAny - or - cc instanceof CallContextReturn -} - -/** - * Resolves a return from `callable` in `cc` to `call`. This is equivalent to - * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. - */ -bindingset[cc, callable] -predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { - cc instanceof CallContextAny and callable = viableCallableExt(call) - or - exists(DataFlowCallable c0, DataFlowCall call0 | - callEnclosingCallable(call0, callable) and - cc = TReturn(c0, call0) and - c0 = prunedViableImplInCallContextReverse(call0, call) - ) -} - -/** - * Resolves a call from `call` in `cc` to `result`. This is equivalent to - * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. - */ -bindingset[call, cc] -DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { - exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | - if reducedViableImplInCallContext(call, _, ctx) - then result = prunedViableImplInCallContext(call, ctx) - else result = viableCallableExt(call) - ) - or - result = viableCallableExt(call) and cc instanceof CallContextSomeCall - or - result = viableCallableExt(call) and cc instanceof CallContextAny - or - result = viableCallableExt(call) and cc instanceof CallContextReturn -} - -/** An optional Boolean value. */ -class BooleanOption extends TBooleanOption { - string toString() { - this = TBooleanNone() and result = "" - or - this = TBooleanSome(any(boolean b | result = b.toString())) - } -} - -/** An optional `DataFlowCall`. */ -class DataFlowCallOption extends TDataFlowCallOption { - string toString() { - this = TDataFlowCallNone() and - result = "(none)" - or - exists(DataFlowCall call | - this = TDataFlowCallSome(call) and - result = call.toString() - ) - } -} - -/** An optional `ParamNode`. */ -class ParamNodeOption extends TParamNodeOption { - string toString() { - this = TParamNodeNone() and - result = "(none)" - or - exists(ParamNode p | - this = TParamNodeSome(p) and - result = p.toString() - ) - } -} - -/** - * A return context used to calculate flow summaries in reverse flow. - * - * The possible values are: - * - * - `TReturnCtxNone()`: no return flow. - * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible. - * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and - * flow through may be possible. - */ -class ReturnCtx extends TReturnCtx { - string toString() { - this = TReturnCtxNone() and - result = "(none)" - or - this = TReturnCtxNoFlowThrough() and - result = "(no flow through)" - or - exists(ReturnPosition pos | - this = TReturnCtxMaybeFlowThrough(pos) and - result = pos.toString() - ) - } -} - -/** - * The front of an approximated access path. This is either a head or a nil. - */ -abstract class ApproxAccessPathFront extends TApproxAccessPathFront { - abstract string toString(); - - abstract boolean toBoolNonEmpty(); - - ContentApprox getHead() { this = TApproxFrontHead(result) } - - pragma[nomagic] - Content getAHead() { - exists(ContentApprox cont | - this = TApproxFrontHead(cont) and - cont = getContentApprox(result) - ) - } -} - -class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil { - override string toString() { result = "nil" } - - override boolean toBoolNonEmpty() { result = false } -} - -class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead { - private ContentApprox c; - - ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) } - - override string toString() { result = c.toString() } - - override boolean toBoolNonEmpty() { result = true } -} - -/** An optional approximated access path front. */ -class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption { - string toString() { - this = TApproxAccessPathFrontNone() and result = "" - or - this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString())) - } -} - -/** - * The front of an access path. This is either a head or a nil. - */ -abstract class AccessPathFront extends TAccessPathFront { - abstract string toString(); - - abstract ApproxAccessPathFront toApprox(); - - Content getHead() { this = TFrontHead(result) } -} - -class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { result = "nil" } - - override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() } -} - -class AccessPathFrontHead extends AccessPathFront, TFrontHead { - private Content c; - - AccessPathFrontHead() { this = TFrontHead(c) } - - override string toString() { result = c.toString() } - - override ApproxAccessPathFront toApprox() { result.getAHead() = c } -} - -/** An optional access path front. */ -class AccessPathFrontOption extends TAccessPathFrontOption { - string toString() { - this = TAccessPathFrontNone() and result = "" - or - this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) - } -} +private import DataFlowImplSpecific +private import codeql.dataflow.internal.DataFlowImplCommon +import MakeImplCommon diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplSpecific.qll index 9244a08582c..e6941afd9d9 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplSpecific.qll @@ -2,6 +2,7 @@ * Provides Swift-specific definitions for use in the data flow library. */ +private import codeql.dataflow.DataFlow // we need to export `Unit` for the DataFlowImpl* files private import swift as Swift @@ -13,3 +14,10 @@ module Private { module Public { import DataFlowPublic } + +module SwiftDataFlow implements InputSig { + import Private + import Public + + Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } +} diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 6b830982d6f..02b32270a88 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -8,13 +8,14 @@ private import codeql.swift.controlflow.BasicBlocks private import codeql.swift.dataflow.FlowSummary as FlowSummary private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import codeql.swift.frameworks.StandardLibrary.PointerTypes +private import codeql.swift.frameworks.StandardLibrary.Array /** Gets the callable in which this node occurs. */ -DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() } +DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ -predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) { - p.isParameterOf(c, pos) +predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + p.(ParameterNodeImpl).isParameterOf(c, pos) } /** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */ @@ -110,7 +111,8 @@ private module Cached { hasExprNode(n, [ any(Argument arg | modifiable(arg)).getExpr(), any(MemberRefExpr ref).getBase(), - any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr() + any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr(), + any(SubscriptExpr se).getBase() ]) } @@ -166,10 +168,18 @@ private module Cached { // flow through `&` (inout argument) nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr() or + // reverse flow through `&` (inout argument) + nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr().(InOutExpr).getSubExpr() = + nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() + or // flow through `try!` and similar constructs nodeFrom.asExpr() = nodeTo.asExpr().(AnyTryExpr).getSubExpr() or // flow through `!` + // note: there's a case in `readStep` that handles when the source is the + // `OptionalSomeContentSet` within the RHS. This case is for when the + // `Optional` itself is tainted (which it usually shouldn't be, but + // retaining this case increases robustness of flow). nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr() or // flow through `?` and `?.` @@ -245,7 +255,8 @@ private module Cached { newtype TContent = TFieldContent(FieldDecl f) or TTupleContent(int index) { exists(any(TupleExpr te).getElement(index)) } or - TEnumContent(ParamDecl f) { exists(EnumElementDecl d | d.getAParam() = f) } + TEnumContent(ParamDecl f) { exists(EnumElementDecl d | d.getAParam() = f) } or + TArrayContent() } /** @@ -691,6 +702,22 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { init.isFailable() ) or + // creation of an array `[v1,v2]` + exists(ArrayExpr arr | + node1.asExpr() = arr.getAnElement() and + node2.asExpr() = arr and + c.isSingleton(any(Content::ArrayContent ac)) + ) + or + // array assignment `a[n] = x` + exists(AssignExpr assign, SubscriptExpr subscript | + node1.asExpr() = assign.getSource() and + node2.(PostUpdateNode).getPreUpdateNode().asExpr() = subscript.getBase() and + subscript = assign.getDest() and + subscript.getBase().getType() instanceof ArrayType and + c.isSingleton(any(Content::ArrayContent ac)) + ) + or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c, node2.(FlowSummaryNode).getSummaryNode()) } @@ -725,6 +752,10 @@ predicate readStep(Node node1, ContentSet c, Node node2) { ) ) or + // read of an enum (`Optional.Some`) member via `!` + node1.asExpr() = node2.asExpr().(ForceValueExpr).getSubExpr() and + c instanceof OptionalSomeContentSet + or // read of a tuple member via `case let (v1, v2)` pattern matching exists(TuplePattern tupPat, int idx, Pattern subPat | node1.asPattern() = tupPat and @@ -742,10 +773,14 @@ predicate readStep(Node node1, ContentSet c, Node node2) { ) or // read of a component in a key-path expression chain - exists(KeyPathComponent component, FieldDecl f | + exists(KeyPathComponent component | component = node1.(KeyPathComponentNodeImpl).getComponent() and - f = component.getDeclRef() and - c.isSingleton(any(Content::FieldContent ct | ct.getField() = f)) + ( + c.isSingleton(any(Content::FieldContent ct | ct.getField() = component.getDeclRef())) + or + c.isSingleton(any(Content::ArrayContent ac)) and + component.isSubscript() + ) | // the next node is either the next element in the chain node2.(KeyPathComponentNodeImpl).getComponent() = component.getNextComponent() @@ -754,6 +789,14 @@ predicate readStep(Node node1, ContentSet c, Node node2) { not exists(component.getNextComponent()) and node2.(KeyPathReturnNodeImpl).getKeyPathExpr() = component.getKeyPathExpr() ) + or + // read of an array member via subscript operator + exists(SubscriptExpr subscript | + subscript.getBase() = node1.asExpr() and + subscript = node2.asExpr() and + subscript.getBase().getType() instanceof ArrayType and + c.isSingleton(any(Content::ArrayContent ac)) + ) } /** @@ -796,7 +839,7 @@ class DataFlowType extends TDataFlowType { predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(NodeImpl n) { +DataFlowType getNodeType(Node n) { any() // return the singleton DataFlowType until we support type pruning for Swift } @@ -849,23 +892,13 @@ class CastNode extends Node { CastNode() { none() } } -/** - * Holds if `n` should never be skipped over in the `PathGraph` and in path - * explanations. - */ -predicate neverSkipInPathGraph(Node n) { none() } - class DataFlowExpr = Expr; -class DataFlowParameter = ParamDecl; - -int accessPathLimit() { result = 5 } - /** * Holds if access paths with `c` at their head always should be tracked at high * precision. This disables adaptive access path precision for such access paths. */ -predicate forceHighPrecision(Content c) { none() } +predicate forceHighPrecision(Content c) { c instanceof Content::ArrayContent } /** * Holds if the node `n` is unreachable when the call context is `call`. diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index a52ddb6f8bd..6fccd145928 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -137,7 +137,7 @@ ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e } /** * Gets the node corresponding to the value of parameter `p` at function entry. */ -ParameterNode parameterNode(DataFlowParameter p) { result.getParameter() = p } +ParameterNode parameterNode(ParamDecl p) { result.getParameter() = p } /** * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local @@ -202,9 +202,27 @@ module Content { /** Gets the declaration of the enum parameter. */ ParamDecl getParam() { result = p } - override string toString() { + /** + * Gets a string describing this enum content, of the form: `EnumElementName:N` where `EnumElementName` + * is the name of the enum element declaration within the enum, and `N` is the 0-based index of the + * parameter that this content is for. For example in the following code there is only one `EnumContent` + * and it's signature is `myValue:0`: + * ``` + * enum MyEnum { + * case myValue(Int) + * } + * ``` + */ + string getSignature() { exists(EnumElementDecl d, int pos | d.getParam(pos) = p | result = d.toString() + ":" + pos) } + + override string toString() { result = this.getSignature() } + } + + /** An element of an array at an unknown index */ + class ArrayContent extends Content, TArrayContent { + override string toString() { result = "Array element" } } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll index 5e4a0ed4b95..ca1b9a316b5 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll @@ -109,6 +109,11 @@ private string getContentSpecific(ContentSet cs) { cs.isSingleton(c) and result = "Field[" + c.getField().getName() + "]" ) + or + exists(Content::ArrayContent c | + cs.isSingleton(c) and + result = "ArrayElement" + ) } /** Gets the textual representation of a summary component in the format used for MaD models. */ diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll b/swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll index 872ac8d4cb8..171a0137682 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll @@ -24,6 +24,7 @@ private module AddTaintDefaults imp Config::allowImplicitRead(node, c) or ( + Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _) or Config::isAdditionalFlowStep(node, _, _, _) diff --git a/swift/ql/lib/codeql/swift/elements/expr/InOutExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/InOutExpr.qll index 0b7fed74a3f..7326cabfdd9 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/InOutExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/InOutExpr.qll @@ -2,4 +2,6 @@ private import codeql.swift.generated.expr.InOutExpr class InOutExpr extends Generated::InOutExpr { override string toString() { result = "&..." } + + override predicate convertsFrom(Expr e) { e = this.getImmediateSubExpr() } } diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Array.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Array.qll new file mode 100644 index 00000000000..207d120e82e --- /dev/null +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Array.qll @@ -0,0 +1,26 @@ +/** + * Provides models for `Array` and related Swift classes. + */ + +import swift +private import codeql.swift.dataflow.ExternalFlow + +/** + * An instance of the `Array` type. + */ +class ArrayType extends Type { + ArrayType() { this.getName().matches("Array<%") or this.getName().matches("[%]") } +} + +/** + * A model for `Array` and related class members that permit data flow. + */ +private class ArraySummaries extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + ";Array;true;insert(_:at:);;;Argument[0];Argument[-1].ArrayElement;value", + ";Array;true;insert(_:at:);;;Argument[1];Argument[-1];taint" + ] + } +} diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll index 109d1acec9c..3e2e8f76714 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll @@ -56,9 +56,7 @@ private class ApplicationWithLaunchOptionsFunc extends Function { private class LaunchOptionsUrlVarDecl extends VarDecl { LaunchOptionsUrlVarDecl() { - // ideally this would be the more accurate, but currently less robust: - // this.getEnclosingDecl().asNominalTypeDecl().getFullName() = "UIApplication.LaunchOptionsKey" and - this.getType().(NominalType).getFullName() = "UIApplication.LaunchOptionsKey" and + this.getEnclosingDecl().asNominalTypeDecl().getFullName() = "UIApplication.LaunchOptionsKey" and this.getName() = "url" } } @@ -83,7 +81,7 @@ private class UserActivityUrlInheritTaint extends TaintInheritingContent, { UserActivityUrlInheritTaint() { this.getField().getEnclosingDecl().asNominalTypeDecl().getName() = "NSUserActivity" and - this.getField().getName() = "webpageURL" + this.getField().getName() = ["webpageURL", "referrerURL"] } } diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Sequence.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Sequence.qll index e830b6cc1a4..da9edb8b3a9 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Sequence.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Sequence.qll @@ -25,6 +25,8 @@ private class SequenceSummaries extends SummaryModelCsv { ";Sequence;true;joined();;;Argument[-1];ReturnValue;taint", ";Sequence;true;joined(separator:);;;Argument[-1..0];ReturnValue;taint", ";Sequence;true;first(where:);;;Argument[-1];ReturnValue;taint", + ";Sequence;true;withContiguousStorageIfAvailable(_:);;;Argument[-1];Argument[0].Parameter[0];taint", + ";Sequence;true;withContiguousStorageIfAvailable(_:);;;Argument[0].ReturnValue;ReturnValue.OptionalSome;value", ] } } diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/StandardLibrary.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/StandardLibrary.qll index 7d529255d37..14fbb3f6006 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/StandardLibrary.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/StandardLibrary.qll @@ -2,6 +2,7 @@ * This file imports all models related to the Swift standard library. */ +private import Array private import Collection private import CustomUrlSchemes private import Data diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll new file mode 100644 index 00000000000..26e8782a799 --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll @@ -0,0 +1,85 @@ +/** + * Provides classes and predicates for reasoning about system + * commands built from user-controlled sources (that is, command injection + * vulnerabilities). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.ExternalFlow + +/** + * A dataflow sink for command injection vulnerabilities. + */ +abstract class CommandInjectionSink extends DataFlow::Node { } + +/** + * A barrier for command injection vulnerabilities. + */ +abstract class CommandInjectionBarrier extends DataFlow::Node { } + +/** + * A unit class for adding additional flow steps. + */ +class CommandInjectionAdditionalFlowStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a flow + * step for paths related to command injection vulnerabilities. + */ + abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); +} + +private class ProcessSink2 extends CommandInjectionSink instanceof DataFlow::Node { + ProcessSink2() { + exists(AssignExpr assign, ProcessHost s | + assign.getDest() = s and + this.asExpr() = assign.getSource() + ) + or + exists(AssignExpr assign, ProcessHost s, ArrayExpr a | + assign.getDest() = s and + a = assign.getSource() and + this.asExpr() = a.getAnElement() + ) + } +} + +private class ProcessHost extends MemberRefExpr { + ProcessHost() { this.getBase() instanceof ProcessRef } +} + +/** An expression of type `Process`. */ +private class ProcessRef extends Expr { + ProcessRef() { + this.getType() instanceof ProcessType or + this.getType() = any(OptionalType t | t.getBaseType() instanceof ProcessType) + } +} + +/** The type `Process`. */ +private class ProcessType extends NominalType { + ProcessType() { this.getFullName() = "Process" } +} + +/** + * A `DataFlow::Node` that is written into a `Process` object. + */ +private class ProcessSink extends CommandInjectionSink instanceof DataFlow::Node { + ProcessSink() { + // any write into a class derived from `Process` is a sink. For + // example in `Process.launchPath = sensitive` the post-update node corresponding + // with `Process.launchPath` is a sink. + exists(NominalType t, Expr e | + t.getABaseType*().getUnderlyingType().getName() = "Process" and + e.getFullyConverted() = this.asExpr() and + e.getFullyConverted().getType() = t + ) + } +} + +/** + * A sink defined in a CSV model. + */ +private class DefaultCommandInjectionSink extends CommandInjectionSink { + DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } +} diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll new file mode 100644 index 00000000000..4b67932209d --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll @@ -0,0 +1,31 @@ +/** + * Provides a taint-tracking configuration for reasoning about system + * commands built from user-controlled sources (that is, Command injection + * vulnerabilities). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import codeql.swift.security.CommandInjectionExtensions + +/** + * A taint configuration for tainted data that reaches a Command Injection sink. + */ +module CommandInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + predicate isSink(DataFlow::Node node) { node instanceof CommandInjectionSink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof CommandInjectionBarrier } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + any(CommandInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + } +} + +/** + * Detect taint flow of tainted data that reaches a Command Injection sink. + */ +module CommandInjectionFlow = TaintTracking::Global; diff --git a/swift/ql/lib/codeql/swift/security/UnsafeJsEvalExtensions.qll b/swift/ql/lib/codeql/swift/security/UnsafeJsEvalExtensions.qll index 61128984cb9..42f69b5d3a5 100644 --- a/swift/ql/lib/codeql/swift/security/UnsafeJsEvalExtensions.qll +++ b/swift/ql/lib/codeql/swift/security/UnsafeJsEvalExtensions.qll @@ -118,9 +118,7 @@ private class DefaultUnsafeJsEvalAdditionalFlowStep extends UnsafeJsEvalAddition ) or exists(CallExpr ce, Expr self, ClosureExpr closure | - ce.getStaticTarget() - .getName() - .matches(["withContiguousStorageIfAvailable(%)", "withUnsafeBufferPointer(%)"]) and + ce.getStaticTarget().getName().matches("withUnsafeBufferPointer(%)") and self = ce.getQualifier() and ce.getArgument(0).getExpr() = closure | diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index 9f87668fce7..1cc6baf95c8 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/swift-all -version: 0.2.1 +version: 0.2.2 groups: swift extractor: swift dbscheme: swift.dbscheme upgrades: upgrades library: true dependencies: + codeql/dataflow: ${workspace} codeql/regex: ${workspace} codeql/mad: ${workspace} codeql/ssa: ${workspace} diff --git a/swift/ql/src/CHANGELOG.md b/swift/ql/src/CHANGELOG.md index 00033599985..115a2266f1e 100644 --- a/swift/ql/src/CHANGELOG.md +++ b/swift/ql/src/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.2.2 + +### New Queries + +* Added new query "Command injection" (`swift/command-line-injection`). The query finds places where user input is used to execute system commands without proper escaping. +* Added new query "Bad HTML filtering regexp" (`swift/bad-tag-filter`). This query finds regular expressions that match HTML tags in a way that is not robust and can easily lead to security issues. + ## 0.2.1 ### New Queries diff --git a/swift/ql/src/change-notes/released/0.2.2.md b/swift/ql/src/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..7735974176e --- /dev/null +++ b/swift/ql/src/change-notes/released/0.2.2.md @@ -0,0 +1,6 @@ +## 0.2.2 + +### New Queries + +* Added new query "Command injection" (`swift/command-line-injection`). The query finds places where user input is used to execute system commands without proper escaping. +* Added new query "Bad HTML filtering regexp" (`swift/bad-tag-filter`). This query finds regular expressions that match HTML tags in a way that is not robust and can easily lead to security issues. diff --git a/swift/ql/src/codeql-pack.release.yml b/swift/ql/src/codeql-pack.release.yml index df29a726bcc..16a06790aa8 100644 --- a/swift/ql/src/codeql-pack.release.yml +++ b/swift/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.1 +lastReleaseVersion: 0.2.2 diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp new file mode 100644 index 00000000000..ab9562af12c --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp @@ -0,0 +1,45 @@ + + + + +

    +Constructing a system command with unsanitized user input is dangerous, +since a malicious user may be able to craft input that executes arbitrary code. +

    +
    + + +

    +If possible, use hard-coded string literals to specify the command to run. Instead of interpreting +user input directly as command names, examine the input and then choose among hard-coded string +literals. +

    +

    +If this is not possible, then add sanitization code to verify that the user input is safe before +using it. +

    +
    + + +

    +The following examples execute code from user input without +sanitizing it first: +

    + +

    +If user input is used to construct a command it should be checked +first. This ensures that the user cannot insert characters that have special +meanings. +

    + +
    + + +
  • +OWASP: +Command Injection. +
  • + + \ No newline at end of file diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql new file mode 100644 index 00000000000..148676e1ac3 --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql @@ -0,0 +1,22 @@ +/** + * @name System command built from user-controlled sources + * @description Building a system command from user-controlled sources is vulnerable to insertion of malicious code by the user. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id swift/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.CommandInjectionQuery +import CommandInjectionFlow::PathGraph + +from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode +where CommandInjectionFlow::flowPath(sourceNode, sinkNode) +select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.", + sourceNode.getNode(), "user-provided value" diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift new file mode 100644 index 00000000000..ffdaaf907ca --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift @@ -0,0 +1,5 @@ +var task = Process() +task.launchPath = "/bin/bash" +task.arguments = ["-c", userControlledString] // BAD + +task.launch() \ No newline at end of file diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift new file mode 100644 index 00000000000..3482718eeac --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift @@ -0,0 +1,13 @@ +func validateCommand(_ command: String) -> String? { + let allowedCommands = ["ls -l", "pwd", "echo"] + if allowedCommands.contains(command) { + return command + } + return nil +} + +var task = Process() +task.launchPath = "/bin/bash" +task.arguments = ["-c", validateCommand(userControlledString)] // GOOD + +task.launch() \ No newline at end of file diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml index 8844d5320a5..05152677233 100644 --- a/swift/ql/src/qlpack.yml +++ b/swift/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-queries -version: 0.2.1 +version: 0.2.2 groups: - swift - queries diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp new file mode 100644 index 00000000000..b406faf8e17 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp @@ -0,0 +1,52 @@ + + + + +

    +It is possible to match some single HTML tags using regular expressions (parsing general HTML using +regular expressions is impossible). However, if the regular expression is not written well, it might +be possible to circumvent it. This can lead to cross-site scripting or other security issues. +

    +

    +Some of these mistakes are caused by browsers having very forgiving HTML parsers, and +will often render invalid HTML containing syntax errors. +Regular expressions that attempt to match HTML should also recognize tags containing such syntax errors. +

    +
    + + +

    +Use a well-tested sanitization or parser library if at all possible. These libraries are much more +likely to handle corner cases correctly than a custom implementation. +

    +
    + + +

    +The following example attempts to filters out all <script> tags. +

    + + + +

    +The above sanitizer does not filter out all <script> tags. +Browsers will not only accept </script> as script end tags, but also tags such as </script foo="bar"> even though it is a parser error. +This means that an attack string such as <script>alert(1)</script foo="bar"> will not be filtered by +the function, and alert(1) will be executed by a browser if the string is rendered as HTML. +

    + +

    +Other corner cases include HTML comments ending with --!>, +and HTML tag names containing uppercase characters. +

    +
    + + +
  • Securitum: The Curious Case of Copy & Paste.
  • +
  • stackoverflow.com: You can't parse [X]HTML with regex.
  • +
  • HTML Standard: Comment end bang state.
  • +
  • stackoverflow.com: Why aren't browsers strict about HTML?
  • +
    +
    diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.ql b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.ql new file mode 100644 index 00000000000..9d7259e77c9 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.ql @@ -0,0 +1,25 @@ +/** + * @name Bad HTML filtering regexp + * @description Matching HTML tags using regular expressions is hard to do right, and can lead to security issues. + * @kind problem + * @problem.severity warning + * @security-severity 7.8 + * @precision high + * @id swift/bad-tag-filter + * @tags correctness + * security + * external/cwe/cwe-116 + * external/cwe/cwe-020 + * external/cwe/cwe-185 + * external/cwe/cwe-186 + */ + +import codeql.swift.regex.Regex +private import codeql.swift.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.BadTagFilterQuery::Make + +from HtmlMatchingRegExp regexp, string msg +where + // there might be multiple messages, we arbitrarily pick the shortest one + msg = min(string m | isBadRegexpFilter(regexp, m) | m order by m.length(), m) +select regexp, msg diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift new file mode 100644 index 00000000000..d399bf5a166 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift @@ -0,0 +1,9 @@ +let script_tag_regex = /]*>.*<\/script>/ + +var old_html = "" +while (html != old_html) { + old_html = html + html.replace(script_tag_regex, with: "") +} + +... diff --git a/swift/ql/src/queries/ide-contextual-queries/printAst.ql b/swift/ql/src/queries/ide-contextual-queries/printAst.ql index 03c106c0051..1541b9f8b0a 100644 --- a/swift/ql/src/queries/ide-contextual-queries/printAst.ql +++ b/swift/ql/src/queries/ide-contextual-queries/printAst.ql @@ -9,7 +9,7 @@ import swift import codeql.swift.printast.PrintAst -import IDEContextual +import codeql.IDEContextual import codeql.swift.generated.ParentChild /** diff --git a/swift/ql/test/extractor-tests/generated/decl/CapturedDecl/PrintAst.expected b/swift/ql/test/extractor-tests/generated/decl/CapturedDecl/PrintAst.expected index 110895e79ff..0c17c568ee7 100644 --- a/swift/ql/test/extractor-tests/generated/decl/CapturedDecl/PrintAst.expected +++ b/swift/ql/test/extractor-tests/generated/decl/CapturedDecl/PrintAst.expected @@ -72,9 +72,9 @@ closures.swift: # 17| getBase(): [TypeExpr] Int.Type # 17| getTypeRepr(): [TypeRepr] Int # 17| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 17| getArgument(0): [Argument] : &... -# 17| getExpr(): [InOutExpr] &... -# 17| getSubExpr(): [DeclRefExpr] x +# 17| getArgument(0): [Argument] : x +# 17| getExpr(): [DeclRefExpr] x +# 17| getExpr().getFullyConverted(): [InOutExpr] &... # 17| getArgument(1): [Argument] : 1 # 17| getExpr(): [IntegerLiteralExpr] 1 # 18| getElement(1): [CallExpr] call to print(_:separator:terminator:) @@ -292,9 +292,9 @@ closures.swift: # 52| getBase(): [TypeExpr] Int.Type # 52| getTypeRepr(): [TypeRepr] Int # 52| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 52| getArgument(0): [Argument] : &... -# 52| getExpr(): [InOutExpr] &... -# 52| getSubExpr(): [DeclRefExpr] x +# 52| getArgument(0): [Argument] : x +# 52| getExpr(): [DeclRefExpr] x +# 52| getExpr().getFullyConverted(): [InOutExpr] &... # 52| getArgument(1): [Argument] : y # 52| getExpr(): [DeclRefExpr] y # 52| getCapture(0): [CapturedDecl] x @@ -304,9 +304,9 @@ closures.swift: # 53| getBase(): [TypeExpr] Int.Type # 53| getTypeRepr(): [TypeRepr] Int # 53| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 53| getArgument(0): [Argument] : &... -# 53| getExpr(): [InOutExpr] &... -# 53| getSubExpr(): [DeclRefExpr] x +# 53| getArgument(0): [Argument] : x +# 53| getExpr(): [DeclRefExpr] x +# 53| getExpr().getFullyConverted(): [InOutExpr] &... # 53| getArgument(1): [Argument] : 40 # 53| getExpr(): [IntegerLiteralExpr] 40 # 54| getElement(3): [PatternBindingDecl] var ... = ... @@ -351,9 +351,9 @@ closures.swift: # 61| getBase(): [TypeExpr] Int.Type # 61| getTypeRepr(): [TypeRepr] Int # 61| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 61| getArgument(0): [Argument] : &... -# 61| getExpr(): [InOutExpr] &... -# 61| getSubExpr(): [DeclRefExpr] x +# 61| getArgument(0): [Argument] : x +# 61| getExpr(): [DeclRefExpr] x +# 61| getExpr().getFullyConverted(): [InOutExpr] &... # 61| getArgument(1): [Argument] : y # 61| getExpr(): [DeclRefExpr] y # 61| getCapture(0): [CapturedDecl] x @@ -363,9 +363,9 @@ closures.swift: # 62| getBase(): [TypeExpr] Int.Type # 62| getTypeRepr(): [TypeRepr] Int # 62| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 62| getArgument(0): [Argument] : &... -# 62| getExpr(): [InOutExpr] &... -# 62| getSubExpr(): [DeclRefExpr] x +# 62| getArgument(0): [Argument] : x +# 62| getExpr(): [DeclRefExpr] x +# 62| getExpr().getFullyConverted(): [InOutExpr] &... # 62| getArgument(1): [Argument] : 40 # 62| getExpr(): [IntegerLiteralExpr] 40 # 63| getElement(3): [PatternBindingDecl] var ... = ... @@ -417,9 +417,9 @@ closures.swift: # 71| getBase(): [TypeExpr] Int.Type # 71| getTypeRepr(): [TypeRepr] Int # 71| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 71| getArgument(0): [Argument] : &... -# 71| getExpr(): [InOutExpr] &... -# 71| getSubExpr(): [DeclRefExpr] x +# 71| getArgument(0): [Argument] : x +# 71| getExpr(): [DeclRefExpr] x +# 71| getExpr().getFullyConverted(): [InOutExpr] &... # 71| getArgument(1): [Argument] : y # 71| getExpr(): [DeclRefExpr] y # 71| getCapture(0): [CapturedDecl] x @@ -429,9 +429,9 @@ closures.swift: # 72| getBase(): [TypeExpr] Int.Type # 72| getTypeRepr(): [TypeRepr] Int # 72| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 72| getArgument(0): [Argument] : &... -# 72| getExpr(): [InOutExpr] &... -# 72| getSubExpr(): [DeclRefExpr] x +# 72| getArgument(0): [Argument] : x +# 72| getExpr(): [DeclRefExpr] x +# 72| getExpr().getFullyConverted(): [InOutExpr] &... # 72| getArgument(1): [Argument] : 40 # 72| getExpr(): [IntegerLiteralExpr] 40 # 73| getElement(3): [PatternBindingDecl] var ... = ... @@ -502,9 +502,9 @@ closures.swift: # 86| getBase(): [TypeExpr] Int.Type # 86| getTypeRepr(): [TypeRepr] Int # 86| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 86| getArgument(0): [Argument] : &... -# 86| getExpr(): [InOutExpr] &... -# 86| getSubExpr(): [DeclRefExpr] x +# 86| getArgument(0): [Argument] : x +# 86| getExpr(): [DeclRefExpr] x +# 86| getExpr().getFullyConverted(): [InOutExpr] &... # 86| getArgument(1): [Argument] : 1 # 86| getExpr(): [IntegerLiteralExpr] 1 # 87| getElement(2): [IfStmt] if ... then { ... } @@ -563,9 +563,9 @@ closures.swift: # 94| getBase(): [TypeExpr] Int.Type # 94| getTypeRepr(): [TypeRepr] Int # 94| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 94| getArgument(0): [Argument] : &... -# 94| getExpr(): [InOutExpr] &... -# 94| getSubExpr(): [DeclRefExpr] x +# 94| getArgument(0): [Argument] : x +# 94| getExpr(): [DeclRefExpr] x +# 94| getExpr().getFullyConverted(): [InOutExpr] &... # 94| getArgument(1): [Argument] : 1 # 94| getExpr(): [IntegerLiteralExpr] 1 # 95| getElement(2): [IfStmt] if ... then { ... } @@ -629,9 +629,9 @@ closures.swift: # 111| getBase(): [TypeExpr] Int.Type # 111| getTypeRepr(): [TypeRepr] Int # 111| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 111| getArgument(0): [Argument] : &... -# 111| getExpr(): [InOutExpr] &... -# 111| getSubExpr(): [DeclRefExpr] x +# 111| getArgument(0): [Argument] : x +# 111| getExpr(): [DeclRefExpr] x +# 111| getExpr().getFullyConverted(): [InOutExpr] &... # 111| getArgument(1): [Argument] : 1 # 111| getExpr(): [IntegerLiteralExpr] 1 # 111| getCapture(0): [CapturedDecl] x diff --git a/swift/ql/test/extractor-tests/generated/expr/MethodLookupExpr/CONSISTENCY/PrintAstConsistency.expected b/swift/ql/test/extractor-tests/generated/expr/MethodLookupExpr/CONSISTENCY/PrintAstConsistency.expected deleted file mode 100644 index 5fff2a2afde..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/MethodLookupExpr/CONSISTENCY/PrintAstConsistency.expected +++ /dev/null @@ -1,3 +0,0 @@ -doubleIndexes -| method_lookups.swift:44:11:44:13 | [AutoClosureExpr] { ... } | 2 | getParam(0) | 4 | getParam(1) | file://:0:0:0:0 | [ParamDecl] argument | -| method_lookups.swift:44:11:44:13 | [AutoClosureExpr] { ... } | 4 | getParam(1) | 2 | getParam(0) | file://:0:0:0:0 | [ParamDecl] argument | diff --git a/swift/ql/test/extractor-tests/generated/type/BuiltinType/builtin_types_dup.swift b/swift/ql/test/extractor-tests/generated/type/BuiltinType/builtin_types_dup.swift new file mode 100644 index 00000000000..a5d07267265 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/BuiltinType/builtin_types_dup.swift @@ -0,0 +1,12 @@ +//codeql-extractor-options: -parse-stdlib +func foo( + _: Builtin.IntLiteral, + _: Builtin.FPIEEE32, + _: Builtin.FPIEEE64, + _: Builtin.BridgeObject, + _: Builtin.NativeObject, + _: Builtin.RawPointer, + _: Builtin.Executor, + _: Builtin.Job, + _: Builtin.RawUnsafeContinuation +) {} diff --git a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType.expected b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType.expected index ba9b94ff4b4..6bd7bc898e9 100644 --- a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType.expected +++ b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType.expected @@ -1,3 +1,7 @@ +| Base | getName: | Base | getCanonicalType: | Base | getInterfaceType: | Base | hasSuperclass: | no | getNumberOfProtocols: | 1 | +| Base | getName: | Base | getCanonicalType: | Base | getInterfaceType: | Base | hasSuperclass: | no | getNumberOfProtocols: | 1 | +| Base | getName: | Base | getCanonicalType: | Base | getInterfaceType: | Base | hasSuperclass: | yes | getNumberOfProtocols: | 0 | +| Base | getName: | Base | getCanonicalType: | Base | getInterfaceType: | Base | hasSuperclass: | yes | getNumberOfProtocols: | 0 | | Param | getName: | Param | getCanonicalType: | Param | getInterfaceType: | Param | hasSuperclass: | no | getNumberOfProtocols: | 0 | | ParamWithProtocols | getName: | ParamWithProtocols | getCanonicalType: | ParamWithProtocols | getInterfaceType: | ParamWithProtocols | hasSuperclass: | no | getNumberOfProtocols: | 2 | | ParamWithSuperclass | getName: | ParamWithSuperclass | getCanonicalType: | ParamWithSuperclass | getInterfaceType: | ParamWithSuperclass | hasSuperclass: | yes | getNumberOfProtocols: | 0 | diff --git a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getProtocol.expected b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getProtocol.expected index c75d7c43c09..361f54b556e 100644 --- a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getProtocol.expected +++ b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getProtocol.expected @@ -1,4 +1,6 @@ +| Base | 0 | primary_archetypes.swift:4:1:4:13 | P | +| Base | 0 | primary_archetypes.swift:5:1:5:14 | P2 | | ParamWithProtocols | 0 | file://:0:0:0:0 | Equatable | -| ParamWithProtocols | 1 | primary_archetypes.swift:3:1:3:13 | P | +| ParamWithProtocols | 1 | primary_archetypes.swift:4:1:4:13 | P | | ParamWithSuperclassAndProtocols | 0 | file://:0:0:0:0 | Equatable | -| ParamWithSuperclassAndProtocols | 1 | primary_archetypes.swift:3:1:3:13 | P | +| ParamWithSuperclassAndProtocols | 1 | primary_archetypes.swift:4:1:4:13 | P | diff --git a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getSuperclass.expected b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getSuperclass.expected index a55689faa97..35f9793baaa 100644 --- a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getSuperclass.expected +++ b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/PrimaryArchetypeType_getSuperclass.expected @@ -1,2 +1,4 @@ +| Base | S | +| Base | S2 | | ParamWithSuperclass | S | | ParamWithSuperclassAndProtocols | S | diff --git a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/primary_archetypes.swift b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/primary_archetypes.swift index db6617226a3..c3468368485 100644 --- a/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/primary_archetypes.swift +++ b/swift/ql/test/extractor-tests/generated/type/PrimaryArchetypeType/primary_archetypes.swift @@ -1,9 +1,25 @@ class S {} +class S2 {} protocol P {} +protocol P2 {} func foo( _: Param, _: ParamWithSuperclass, _: ParamWithProtocols, _: ParamWithSuperclassAndProtocols) {} + +class Generic {} +extension Generic where Base : P { + func f(_: Base) {} +} +extension Generic where Base : P2 { + func f(_: Base) {} +} +extension Generic where Base : S { + func f(_: Base) {} +} +extension Generic where Base : S2 { + func f(_: Base) {} +} diff --git a/swift/ql/test/extractor-tests/generated/type/TupleType/tuples_builtin_dup.swift b/swift/ql/test/extractor-tests/generated/type/TupleType/tuples_builtin_dup.swift new file mode 100644 index 00000000000..1b5bb888c5f --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/TupleType/tuples_builtin_dup.swift @@ -0,0 +1,2 @@ +//codeql-extractor-options: -parse-stdlib +func f(x : (Builtin.IntLiteral, Builtin.IntLiteral)) {} diff --git a/swift/ql/test/extractor-tests/patterns/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/patterns/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 1ad4ad0b970..00000000000 --- a/swift/ql/test/extractor-tests/patterns/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| patterns.swift:16:10:16:14 | =~ ... | diff --git a/swift/ql/test/extractor-tests/statements/YieldStmts.expected b/swift/ql/test/extractor-tests/statements/YieldStmts.expected index 770c90018d8..aef0559179b 100644 --- a/swift/ql/test/extractor-tests/statements/YieldStmts.expected +++ b/swift/ql/test/extractor-tests/statements/YieldStmts.expected @@ -1,2 +1,2 @@ -| statements.swift:75:7:75:7 | yield ... | 0 | file://:0:0:0:0 | &... | -| statements.swift:78:7:78:14 | yield ... | 0 | statements.swift:78:13:78:14 | &... | +| statements.swift:75:7:75:7 | yield ... | 0 | file://:0:0:0:0 | .x | +| statements.swift:78:7:78:14 | yield ... | 0 | statements.swift:78:14:78:14 | .x | diff --git a/swift/ql/test/extractor-tests/updates/PrintAst.expected b/swift/ql/test/extractor-tests/updates/PrintAst.expected index 7c9962f5d64..1edb868b256 100644 --- a/swift/ql/test/extractor-tests/updates/PrintAst.expected +++ b/swift/ql/test/extractor-tests/updates/PrintAst.expected @@ -33,9 +33,9 @@ v5.8.swift: # 5| Type = Temperature # 5| getBody(): [BraceStmt] { ... } # 5| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .degreesCelsius -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .degreesCelsius +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 4| getMember(2): [Initializer] Temperature.init(degreesCelsius:) # 4| InterfaceType = (Temperature.Type) -> (Double) -> Temperature # 4| getSelfParam(): [ParamDecl] self @@ -145,9 +145,9 @@ v5.8.swift: # 30| Type = Button # 30| getBody(): [BraceStmt] { ... } # 30| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .tapHandler -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .tapHandler +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 29| getMember(2): [Initializer] Button.init() # 29| InterfaceType = (Button.Type) -> () -> Button # 29| getSelfParam(): [ParamDecl] self @@ -197,9 +197,9 @@ v5.8.swift: # 34| Type = ViewController # 34| getBody(): [BraceStmt] { ... } # 34| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .button -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .button +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 36| getMember(2): [NamedFunction] setup() # 36| InterfaceType = (ViewController) -> () -> () # 36| getSelfParam(): [ParamDecl] self diff --git a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected index 9c49013832a..81ad590aaab 100644 --- a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected @@ -8,4 +8,3 @@ multipleSuccessors deadEnd | cfg.swift:33:49:33:60 | call to isZero(x:) | | cfg.swift:144:18:144:34 | ... .&&(_:_:) ... | -| patterns.swift:16:10:16:14 | =~ ... | diff --git a/swift/ql/test/library-tests/ast/Errors.expected b/swift/ql/test/library-tests/ast/Errors.expected index a65eb1b2466..e69de29bb2d 100644 --- a/swift/ql/test/library-tests/ast/Errors.expected +++ b/swift/ql/test/library-tests/ast/Errors.expected @@ -1 +0,0 @@ -| patterns.swift:16:12:16:12 | OverloadedDeclRefExpr | OverloadedDeclRefExpr | diff --git a/swift/ql/test/library-tests/ast/PrintAst.expected b/swift/ql/test/library-tests/ast/PrintAst.expected index 39320293cbc..4f962606e1b 100644 --- a/swift/ql/test/library-tests/ast/PrintAst.expected +++ b/swift/ql/test/library-tests/ast/PrintAst.expected @@ -213,22 +213,22 @@ cfg.swift: #-----| Type = DefaultStringInterpolation # 40| getElement(0): [CallExpr] call to appendLiteral(_:) # 40| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 40| getBase(): [InOutExpr] &... -# 40| getSubExpr(): [DeclRefExpr] $interpolation +# 40| getBase(): [DeclRefExpr] $interpolation +# 40| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 40| getArgument(0): [Argument] : Unknown error # 40| getExpr(): [StringLiteralExpr] Unknown error # 40| getElement(1): [CallExpr] call to appendInterpolation(_:) # 40| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 40| getBase(): [InOutExpr] &... -# 40| getSubExpr(): [DeclRefExpr] $interpolation +# 40| getBase(): [DeclRefExpr] $interpolation +# 40| getBase().getFullyConverted(): [InOutExpr] &... # 40| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 40| getArgument(0): [Argument] : error # 40| getExpr(): [DeclRefExpr] error # 40| getElement(2): [CallExpr] call to appendLiteral(_:) # 40| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 40| getBase(): [InOutExpr] &... -# 40| getSubExpr(): [DeclRefExpr] $interpolation +# 40| getBase(): [DeclRefExpr] $interpolation +# 40| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 40| getArgument(0): [Argument] : # 40| getExpr(): [StringLiteralExpr] @@ -435,9 +435,9 @@ cfg.swift: # 89| getSource(): [NilLiteralExpr] nil # 92| getElement(3): [CallExpr] call to add(a:) # 92| getFunction(): [DeclRefExpr] add(a:) -# 92| getArgument(0): [Argument] a: &... -# 92| getExpr(): [InOutExpr] &... -# 92| getSubExpr(): [DeclRefExpr] temp +# 92| getArgument(0): [Argument] a: temp +# 92| getExpr(): [DeclRefExpr] temp +# 92| getExpr().getFullyConverted(): [InOutExpr] &... # 93| getElement(4): [PatternBindingDecl] var ... = ... # 93| getInit(0): [IntegerLiteralExpr] 10 # 93| getInit(0).getFullyConverted(): [InjectIntoOptionalExpr] (Int?) ... @@ -446,9 +446,9 @@ cfg.swift: # 93| getTypeRepr(): [TypeRepr] Int? # 94| getElement(5): [CallExpr] call to addOptional(a:) # 94| getFunction(): [DeclRefExpr] addOptional(a:) -# 94| getArgument(0): [Argument] a: &... -# 94| getExpr(): [InOutExpr] &... -# 94| getSubExpr(): [DeclRefExpr] tempOptional +# 94| getArgument(0): [Argument] a: tempOptional +# 94| getExpr(): [DeclRefExpr] tempOptional +# 94| getExpr().getFullyConverted(): [InOutExpr] &... # 95| getElement(6): [ReturnStmt] return ... # 95| getResult(): [BinaryExpr] ... .+(_:_:) ... # 95| getFunction(): [MethodLookupExpr] .+(_:_:) @@ -723,17 +723,32 @@ cfg.swift: # 143| getResult(): [BooleanLiteralExpr] true # 141| getLabel(0): [CaseLabelItem] =~ ... # 141| getPattern(): [ExprPattern] =~ ... -# 141| getSubExpr(): [IntegerLiteralExpr] 0 +# 141| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 141| getFunction(): [DeclRefExpr] ~=(_:_:) +# 141| getArgument(0): [Argument] : 0 +# 141| getExpr(): [IntegerLiteralExpr] 0 +# 141| getArgument(1): [Argument] : $match +# 141| getExpr(): [DeclRefExpr] $match # 141| getLabel(1): [CaseLabelItem] =~ ... # 141| getPattern(): [ExprPattern] =~ ... -# 141| getSubExpr(): [IntegerLiteralExpr] 1 +# 141| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 141| getFunction(): [DeclRefExpr] ~=(_:_:) +# 141| getArgument(0): [Argument] : 1 +# 141| getExpr(): [IntegerLiteralExpr] 1 +# 141| getArgument(1): [Argument] : $match +# 141| getExpr(): [DeclRefExpr] $match # 144| getCase(1): [CaseStmt] case ... # 145| getBody(): [BraceStmt] { ... } # 145| getElement(0): [ReturnStmt] return ... # 145| getResult(): [BooleanLiteralExpr] true # 144| getLabel(0): [CaseLabelItem] =~ ... where ... # 144| getPattern(): [ExprPattern] =~ ... -# 144| getSubExpr(): [DeclRefExpr] x +# 144| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 144| getFunction(): [DeclRefExpr] ~=(_:_:) +# 144| getArgument(0): [Argument] : x +# 144| getExpr(): [DeclRefExpr] x +# 144| getArgument(1): [Argument] : $match +# 144| getExpr(): [DeclRefExpr] $match # 144| getGuard(): [BinaryExpr] ... .&&(_:_:) ... # 144| getFunction(): [MethodLookupExpr] .&&(_:_:) # 144| getBase(): [TypeExpr] Bool.Type @@ -819,6 +834,12 @@ cfg.swift: # 158| getElse(): [BraceStmt] { ... } # 159| getElement(0): [ReturnStmt] return ... # 159| getResult(): [BooleanLiteralExpr] false +# 141| [ConcreteVarDecl] $match +# 141| Type = Int +# 141| [ConcreteVarDecl] $match +# 141| Type = Int +# 144| [ConcreteVarDecl] $match +# 144| Type = Int # 156| [ConcreteVarDecl] x # 156| Type = (Int) # 163| [NamedFunction] testDefer(x:) @@ -1427,43 +1448,43 @@ cfg.swift: #-----| Type = DefaultStringInterpolation # 263| getElement(0): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 263| getArgument(0): [Argument] : # 263| getExpr(): [StringLiteralExpr] # 263| getElement(1): [CallExpr] call to appendInterpolation(_:) # 263| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... # 263| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 263| getArgument(0): [Argument] : x # 263| getExpr(): [DeclRefExpr] x # 263| getElement(2): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 263| getArgument(0): [Argument] : + # 263| getExpr(): [StringLiteralExpr] + # 263| getElement(3): [CallExpr] call to appendInterpolation(_:) # 263| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... # 263| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 263| getArgument(0): [Argument] : y # 263| getExpr(): [DeclRefExpr] y # 263| getElement(4): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 263| getArgument(0): [Argument] : is equal to # 263| getExpr(): [StringLiteralExpr] is equal to # 263| getElement(5): [CallExpr] call to appendInterpolation(_:) # 263| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... # 263| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 263| getArgument(0): [Argument] : ... .+(_:_:) ... # 263| getExpr(): [BinaryExpr] ... .+(_:_:) ... @@ -1477,23 +1498,23 @@ cfg.swift: # 263| getExpr(): [DeclRefExpr] y # 263| getElement(6): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 263| getArgument(0): [Argument] : and here is a zero: # 263| getExpr(): [StringLiteralExpr] and here is a zero: # 263| getElement(7): [CallExpr] call to appendInterpolation(_:) # 263| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... # 263| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 263| getArgument(0): [Argument] : call to returnZero() # 263| getExpr(): [CallExpr] call to returnZero() # 263| getFunction(): [DeclRefExpr] returnZero() # 263| getElement(8): [CallExpr] call to appendLiteral(_:) # 263| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 263| getBase(): [InOutExpr] &... -# 263| getSubExpr(): [DeclRefExpr] $interpolation +# 263| getBase(): [DeclRefExpr] $interpolation +# 263| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 263| getArgument(0): [Argument] : # 263| getExpr(): [StringLiteralExpr] @@ -1532,8 +1553,8 @@ cfg.swift: # 267| getPattern(0): [NamedPattern] a # 268| getElement(1): [AssignExpr] ... = ... # 268| getDest(): [SubscriptExpr] ...[...] -# 268| getBase(): [InOutExpr] &... -# 268| getSubExpr(): [DeclRefExpr] a +# 268| getBase(): [DeclRefExpr] a +# 268| getBase().getFullyConverted(): [InOutExpr] &... # 268| getArgument(0): [Argument] : 0 # 268| getExpr(): [IntegerLiteralExpr] 0 # 268| getSource(): [IntegerLiteralExpr] 0 @@ -1542,13 +1563,13 @@ cfg.swift: # 269| getBase(): [TypeExpr] Int.Type # 269| getTypeRepr(): [TypeRepr] Int # 269| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 269| getArgument(0): [Argument] : &... -# 269| getExpr(): [InOutExpr] &... -# 269| getSubExpr(): [SubscriptExpr] ...[...] -# 269| getBase(): [InOutExpr] &... -# 269| getSubExpr(): [DeclRefExpr] a -# 269| getArgument(0): [Argument] : 1 -# 269| getExpr(): [IntegerLiteralExpr] 1 +# 269| getArgument(0): [Argument] : ...[...] +# 269| getExpr(): [SubscriptExpr] ...[...] +# 269| getBase(): [DeclRefExpr] a +# 269| getBase().getFullyConverted(): [InOutExpr] &... +# 269| getArgument(0): [Argument] : 1 +# 269| getExpr(): [IntegerLiteralExpr] 1 +# 269| getExpr().getFullyConverted(): [InOutExpr] &... # 269| getArgument(1): [Argument] : 1 # 269| getExpr(): [IntegerLiteralExpr] 1 # 270| getElement(3): [BinaryExpr] ... .-=(_:_:) ... @@ -1556,13 +1577,13 @@ cfg.swift: # 270| getBase(): [TypeExpr] Int.Type # 270| getTypeRepr(): [TypeRepr] Int # 270| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 270| getArgument(0): [Argument] : &... -# 270| getExpr(): [InOutExpr] &... -# 270| getSubExpr(): [SubscriptExpr] ...[...] -# 270| getBase(): [InOutExpr] &... -# 270| getSubExpr(): [DeclRefExpr] a -# 270| getArgument(0): [Argument] : 2 -# 270| getExpr(): [IntegerLiteralExpr] 2 +# 270| getArgument(0): [Argument] : ...[...] +# 270| getExpr(): [SubscriptExpr] ...[...] +# 270| getBase(): [DeclRefExpr] a +# 270| getBase().getFullyConverted(): [InOutExpr] &... +# 270| getArgument(0): [Argument] : 2 +# 270| getExpr(): [IntegerLiteralExpr] 2 +# 270| getExpr().getFullyConverted(): [InOutExpr] &... # 270| getArgument(1): [Argument] : 1 # 270| getExpr(): [IntegerLiteralExpr] 1 # 271| getElement(4): [BinaryExpr] ... .*=(_:_:) ... @@ -1570,13 +1591,13 @@ cfg.swift: # 271| getBase(): [TypeExpr] Int.Type # 271| getTypeRepr(): [TypeRepr] Int # 271| getMethodRef(): [DeclRefExpr] *=(_:_:) -# 271| getArgument(0): [Argument] : &... -# 271| getExpr(): [InOutExpr] &... -# 271| getSubExpr(): [SubscriptExpr] ...[...] -# 271| getBase(): [InOutExpr] &... -# 271| getSubExpr(): [DeclRefExpr] a -# 271| getArgument(0): [Argument] : 3 -# 271| getExpr(): [IntegerLiteralExpr] 3 +# 271| getArgument(0): [Argument] : ...[...] +# 271| getExpr(): [SubscriptExpr] ...[...] +# 271| getBase(): [DeclRefExpr] a +# 271| getBase().getFullyConverted(): [InOutExpr] &... +# 271| getArgument(0): [Argument] : 3 +# 271| getExpr(): [IntegerLiteralExpr] 3 +# 271| getExpr().getFullyConverted(): [InOutExpr] &... # 271| getArgument(1): [Argument] : 1 # 271| getExpr(): [IntegerLiteralExpr] 1 # 272| getElement(5): [BinaryExpr] ... ./=(_:_:) ... @@ -1584,13 +1605,13 @@ cfg.swift: # 272| getBase(): [TypeExpr] Int.Type # 272| getTypeRepr(): [TypeRepr] Int # 272| getMethodRef(): [DeclRefExpr] /=(_:_:) -# 272| getArgument(0): [Argument] : &... -# 272| getExpr(): [InOutExpr] &... -# 272| getSubExpr(): [SubscriptExpr] ...[...] -# 272| getBase(): [InOutExpr] &... -# 272| getSubExpr(): [DeclRefExpr] a -# 272| getArgument(0): [Argument] : 4 -# 272| getExpr(): [IntegerLiteralExpr] 4 +# 272| getArgument(0): [Argument] : ...[...] +# 272| getExpr(): [SubscriptExpr] ...[...] +# 272| getBase(): [DeclRefExpr] a +# 272| getBase().getFullyConverted(): [InOutExpr] &... +# 272| getArgument(0): [Argument] : 4 +# 272| getExpr(): [IntegerLiteralExpr] 4 +# 272| getExpr().getFullyConverted(): [InOutExpr] &... # 272| getArgument(1): [Argument] : 1 # 272| getExpr(): [IntegerLiteralExpr] 1 # 273| getElement(6): [BinaryExpr] ... .%=(_:_:) ... @@ -1598,13 +1619,13 @@ cfg.swift: # 273| getBase(): [TypeExpr] Int.Type # 273| getTypeRepr(): [TypeRepr] Int # 273| getMethodRef(): [DeclRefExpr] %=(_:_:) -# 273| getArgument(0): [Argument] : &... -# 273| getExpr(): [InOutExpr] &... -# 273| getSubExpr(): [SubscriptExpr] ...[...] -# 273| getBase(): [InOutExpr] &... -# 273| getSubExpr(): [DeclRefExpr] a -# 273| getArgument(0): [Argument] : 5 -# 273| getExpr(): [IntegerLiteralExpr] 5 +# 273| getArgument(0): [Argument] : ...[...] +# 273| getExpr(): [SubscriptExpr] ...[...] +# 273| getBase(): [DeclRefExpr] a +# 273| getBase().getFullyConverted(): [InOutExpr] &... +# 273| getArgument(0): [Argument] : 5 +# 273| getExpr(): [IntegerLiteralExpr] 5 +# 273| getExpr().getFullyConverted(): [InOutExpr] &... # 273| getArgument(1): [Argument] : 1 # 273| getExpr(): [IntegerLiteralExpr] 1 # 274| getElement(7): [BinaryExpr] ... .&=(_:_:) ... @@ -1612,13 +1633,13 @@ cfg.swift: # 274| getBase(): [TypeExpr] Int.Type # 274| getTypeRepr(): [TypeRepr] Int # 274| getMethodRef(): [DeclRefExpr] &=(_:_:) -# 274| getArgument(0): [Argument] : &... -# 274| getExpr(): [InOutExpr] &... -# 274| getSubExpr(): [SubscriptExpr] ...[...] -# 274| getBase(): [InOutExpr] &... -# 274| getSubExpr(): [DeclRefExpr] a -# 274| getArgument(0): [Argument] : 6 -# 274| getExpr(): [IntegerLiteralExpr] 6 +# 274| getArgument(0): [Argument] : ...[...] +# 274| getExpr(): [SubscriptExpr] ...[...] +# 274| getBase(): [DeclRefExpr] a +# 274| getBase().getFullyConverted(): [InOutExpr] &... +# 274| getArgument(0): [Argument] : 6 +# 274| getExpr(): [IntegerLiteralExpr] 6 +# 274| getExpr().getFullyConverted(): [InOutExpr] &... # 274| getArgument(1): [Argument] : 1 # 274| getExpr(): [IntegerLiteralExpr] 1 # 275| getElement(8): [BinaryExpr] ... .|=(_:_:) ... @@ -1626,13 +1647,13 @@ cfg.swift: # 275| getBase(): [TypeExpr] Int.Type # 275| getTypeRepr(): [TypeRepr] Int # 275| getMethodRef(): [DeclRefExpr] |=(_:_:) -# 275| getArgument(0): [Argument] : &... -# 275| getExpr(): [InOutExpr] &... -# 275| getSubExpr(): [SubscriptExpr] ...[...] -# 275| getBase(): [InOutExpr] &... -# 275| getSubExpr(): [DeclRefExpr] a -# 275| getArgument(0): [Argument] : 7 -# 275| getExpr(): [IntegerLiteralExpr] 7 +# 275| getArgument(0): [Argument] : ...[...] +# 275| getExpr(): [SubscriptExpr] ...[...] +# 275| getBase(): [DeclRefExpr] a +# 275| getBase().getFullyConverted(): [InOutExpr] &... +# 275| getArgument(0): [Argument] : 7 +# 275| getExpr(): [IntegerLiteralExpr] 7 +# 275| getExpr().getFullyConverted(): [InOutExpr] &... # 275| getArgument(1): [Argument] : 1 # 275| getExpr(): [IntegerLiteralExpr] 1 # 276| getElement(9): [BinaryExpr] ... .^=(_:_:) ... @@ -1640,13 +1661,13 @@ cfg.swift: # 276| getBase(): [TypeExpr] Int.Type # 276| getTypeRepr(): [TypeRepr] Int # 276| getMethodRef(): [DeclRefExpr] ^=(_:_:) -# 276| getArgument(0): [Argument] : &... -# 276| getExpr(): [InOutExpr] &... -# 276| getSubExpr(): [SubscriptExpr] ...[...] -# 276| getBase(): [InOutExpr] &... -# 276| getSubExpr(): [DeclRefExpr] a -# 276| getArgument(0): [Argument] : 8 -# 276| getExpr(): [IntegerLiteralExpr] 8 +# 276| getArgument(0): [Argument] : ...[...] +# 276| getExpr(): [SubscriptExpr] ...[...] +# 276| getBase(): [DeclRefExpr] a +# 276| getBase().getFullyConverted(): [InOutExpr] &... +# 276| getArgument(0): [Argument] : 8 +# 276| getExpr(): [IntegerLiteralExpr] 8 +# 276| getExpr().getFullyConverted(): [InOutExpr] &... # 276| getArgument(1): [Argument] : 1 # 276| getExpr(): [IntegerLiteralExpr] 1 # 277| getElement(10): [BinaryExpr] ... .<<=(_:_:) ... @@ -1654,13 +1675,13 @@ cfg.swift: # 277| getBase(): [TypeExpr] Int.Type # 277| getTypeRepr(): [TypeRepr] Int # 277| getMethodRef(): [DeclRefExpr] <<=(_:_:) -# 277| getArgument(0): [Argument] : &... -# 277| getExpr(): [InOutExpr] &... -# 277| getSubExpr(): [SubscriptExpr] ...[...] -# 277| getBase(): [InOutExpr] &... -# 277| getSubExpr(): [DeclRefExpr] a -# 277| getArgument(0): [Argument] : 9 -# 277| getExpr(): [IntegerLiteralExpr] 9 +# 277| getArgument(0): [Argument] : ...[...] +# 277| getExpr(): [SubscriptExpr] ...[...] +# 277| getBase(): [DeclRefExpr] a +# 277| getBase().getFullyConverted(): [InOutExpr] &... +# 277| getArgument(0): [Argument] : 9 +# 277| getExpr(): [IntegerLiteralExpr] 9 +# 277| getExpr().getFullyConverted(): [InOutExpr] &... # 277| getArgument(1): [Argument] : 1 # 277| getExpr(): [IntegerLiteralExpr] 1 # 278| getElement(11): [BinaryExpr] ... .>>=(_:_:) ... @@ -1668,44 +1689,44 @@ cfg.swift: # 278| getBase(): [TypeExpr] Int.Type # 278| getTypeRepr(): [TypeRepr] Int # 278| getMethodRef(): [DeclRefExpr] >>=(_:_:) -# 278| getArgument(0): [Argument] : &... -# 278| getExpr(): [InOutExpr] &... -# 278| getSubExpr(): [SubscriptExpr] ...[...] -# 278| getBase(): [InOutExpr] &... -# 278| getSubExpr(): [DeclRefExpr] a -# 278| getArgument(0): [Argument] : 10 -# 278| getExpr(): [IntegerLiteralExpr] 10 +# 278| getArgument(0): [Argument] : ...[...] +# 278| getExpr(): [SubscriptExpr] ...[...] +# 278| getBase(): [DeclRefExpr] a +# 278| getBase().getFullyConverted(): [InOutExpr] &... +# 278| getArgument(0): [Argument] : 10 +# 278| getExpr(): [IntegerLiteralExpr] 10 +# 278| getExpr().getFullyConverted(): [InOutExpr] &... # 278| getArgument(1): [Argument] : 1 # 278| getExpr(): [IntegerLiteralExpr] 1 # 280| getElement(12): [PatternBindingDecl] var ... = ... # 280| getInit(0): [TupleExpr] (...) # 280| getElement(0): [SubscriptExpr] ...[...] -# 280| getBase(): [InOutExpr] &... -# 280| getSubExpr(): [DeclRefExpr] a +# 280| getBase(): [DeclRefExpr] a +# 280| getBase().getFullyConverted(): [InOutExpr] &... # 280| getArgument(0): [Argument] : 0 # 280| getExpr(): [IntegerLiteralExpr] 0 # 280| getElement(0).getFullyConverted(): [LoadExpr] (Int) ... # 280| getElement(1): [SubscriptExpr] ...[...] -# 280| getBase(): [InOutExpr] &... -# 280| getSubExpr(): [DeclRefExpr] a +# 280| getBase(): [DeclRefExpr] a +# 280| getBase().getFullyConverted(): [InOutExpr] &... # 280| getArgument(0): [Argument] : 1 # 280| getExpr(): [IntegerLiteralExpr] 1 # 280| getElement(1).getFullyConverted(): [LoadExpr] (Int) ... # 280| getElement(2): [SubscriptExpr] ...[...] -# 280| getBase(): [InOutExpr] &... -# 280| getSubExpr(): [DeclRefExpr] a +# 280| getBase(): [DeclRefExpr] a +# 280| getBase().getFullyConverted(): [InOutExpr] &... # 280| getArgument(0): [Argument] : 2 # 280| getExpr(): [IntegerLiteralExpr] 2 # 280| getElement(2).getFullyConverted(): [LoadExpr] (Int) ... # 280| getElement(3): [SubscriptExpr] ...[...] -# 280| getBase(): [InOutExpr] &... -# 280| getSubExpr(): [DeclRefExpr] a +# 280| getBase(): [DeclRefExpr] a +# 280| getBase().getFullyConverted(): [InOutExpr] &... # 280| getArgument(0): [Argument] : 3 # 280| getExpr(): [IntegerLiteralExpr] 3 # 280| getElement(3).getFullyConverted(): [LoadExpr] (Int) ... # 280| getElement(4): [SubscriptExpr] ...[...] -# 280| getBase(): [InOutExpr] &... -# 280| getSubExpr(): [DeclRefExpr] a +# 280| getBase(): [DeclRefExpr] a +# 280| getBase().getFullyConverted(): [InOutExpr] &... # 280| getArgument(0): [Argument] : 4 # 280| getExpr(): [IntegerLiteralExpr] 4 # 280| getElement(4).getFullyConverted(): [LoadExpr] (Int) ... @@ -1727,20 +1748,20 @@ cfg.swift: # 282| getPattern(0): [NamedPattern] b # 283| getElement(14): [AssignExpr] ... = ... # 283| getDest(): [SubscriptExpr] ...[...] -# 283| getBase(): [InOutExpr] &... -# 283| getSubExpr(): [DeclRefExpr] b +# 283| getBase(): [DeclRefExpr] b +# 283| getBase().getFullyConverted(): [InOutExpr] &... # 283| getArgument(0): [Argument] : 0 # 283| getExpr(): [IntegerLiteralExpr] 0 # 283| getSource(): [SubscriptExpr] ...[...] -# 283| getBase(): [InOutExpr] &... -# 283| getSubExpr(): [DeclRefExpr] a +# 283| getBase(): [DeclRefExpr] a +# 283| getBase().getFullyConverted(): [InOutExpr] &... # 283| getArgument(0): [Argument] : 10 # 283| getExpr(): [IntegerLiteralExpr] 10 # 283| getSource().getFullyConverted(): [LoadExpr] (Int) ... # 284| getElement(15): [AssignExpr] ... = ... # 284| getDest(): [SubscriptExpr] ...[...] -# 284| getBase(): [InOutExpr] &... -# 284| getSubExpr(): [DeclRefExpr] b +# 284| getBase(): [DeclRefExpr] b +# 284| getBase().getFullyConverted(): [InOutExpr] &... # 284| getArgument(0): [Argument] : 1 # 284| getExpr(): [IntegerLiteralExpr] 1 # 284| getSource(): [BinaryExpr] ... .+(_:_:) ... @@ -1750,8 +1771,8 @@ cfg.swift: # 284| getMethodRef(): [DeclRefExpr] +(_:_:) # 284| getArgument(0): [Argument] : ...[...] # 284| getExpr(): [SubscriptExpr] ...[...] -# 284| getBase(): [InOutExpr] &... -# 284| getSubExpr(): [DeclRefExpr] b +# 284| getBase(): [DeclRefExpr] b +# 284| getBase().getFullyConverted(): [InOutExpr] &... # 284| getArgument(0): [Argument] : 0 # 284| getExpr(): [IntegerLiteralExpr] 0 # 284| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1759,8 +1780,8 @@ cfg.swift: # 284| getExpr(): [IntegerLiteralExpr] 1 # 285| getElement(16): [AssignExpr] ... = ... # 285| getDest(): [SubscriptExpr] ...[...] -# 285| getBase(): [InOutExpr] &... -# 285| getSubExpr(): [DeclRefExpr] b +# 285| getBase(): [DeclRefExpr] b +# 285| getBase().getFullyConverted(): [InOutExpr] &... # 285| getArgument(0): [Argument] : 2 # 285| getExpr(): [IntegerLiteralExpr] 2 # 285| getSource(): [BinaryExpr] ... .-(_:_:) ... @@ -1770,8 +1791,8 @@ cfg.swift: # 285| getMethodRef(): [DeclRefExpr] -(_:_:) # 285| getArgument(0): [Argument] : ...[...] # 285| getExpr(): [SubscriptExpr] ...[...] -# 285| getBase(): [InOutExpr] &... -# 285| getSubExpr(): [DeclRefExpr] b +# 285| getBase(): [DeclRefExpr] b +# 285| getBase().getFullyConverted(): [InOutExpr] &... # 285| getArgument(0): [Argument] : 1 # 285| getExpr(): [IntegerLiteralExpr] 1 # 285| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1779,8 +1800,8 @@ cfg.swift: # 285| getExpr(): [IntegerLiteralExpr] 1 # 286| getElement(17): [AssignExpr] ... = ... # 286| getDest(): [SubscriptExpr] ...[...] -# 286| getBase(): [InOutExpr] &... -# 286| getSubExpr(): [DeclRefExpr] b +# 286| getBase(): [DeclRefExpr] b +# 286| getBase().getFullyConverted(): [InOutExpr] &... # 286| getArgument(0): [Argument] : 3 # 286| getExpr(): [IntegerLiteralExpr] 3 # 286| getSource(): [BinaryExpr] ... .*(_:_:) ... @@ -1790,8 +1811,8 @@ cfg.swift: # 286| getMethodRef(): [DeclRefExpr] *(_:_:) # 286| getArgument(0): [Argument] : ...[...] # 286| getExpr(): [SubscriptExpr] ...[...] -# 286| getBase(): [InOutExpr] &... -# 286| getSubExpr(): [DeclRefExpr] b +# 286| getBase(): [DeclRefExpr] b +# 286| getBase().getFullyConverted(): [InOutExpr] &... # 286| getArgument(0): [Argument] : 2 # 286| getExpr(): [IntegerLiteralExpr] 2 # 286| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1799,8 +1820,8 @@ cfg.swift: # 286| getExpr(): [IntegerLiteralExpr] 1 # 287| getElement(18): [AssignExpr] ... = ... # 287| getDest(): [SubscriptExpr] ...[...] -# 287| getBase(): [InOutExpr] &... -# 287| getSubExpr(): [DeclRefExpr] b +# 287| getBase(): [DeclRefExpr] b +# 287| getBase().getFullyConverted(): [InOutExpr] &... # 287| getArgument(0): [Argument] : 4 # 287| getExpr(): [IntegerLiteralExpr] 4 # 287| getSource(): [BinaryExpr] ... ./(_:_:) ... @@ -1810,8 +1831,8 @@ cfg.swift: # 287| getMethodRef(): [DeclRefExpr] /(_:_:) # 287| getArgument(0): [Argument] : ...[...] # 287| getExpr(): [SubscriptExpr] ...[...] -# 287| getBase(): [InOutExpr] &... -# 287| getSubExpr(): [DeclRefExpr] b +# 287| getBase(): [DeclRefExpr] b +# 287| getBase().getFullyConverted(): [InOutExpr] &... # 287| getArgument(0): [Argument] : 3 # 287| getExpr(): [IntegerLiteralExpr] 3 # 287| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1819,8 +1840,8 @@ cfg.swift: # 287| getExpr(): [IntegerLiteralExpr] 1 # 288| getElement(19): [AssignExpr] ... = ... # 288| getDest(): [SubscriptExpr] ...[...] -# 288| getBase(): [InOutExpr] &... -# 288| getSubExpr(): [DeclRefExpr] b +# 288| getBase(): [DeclRefExpr] b +# 288| getBase().getFullyConverted(): [InOutExpr] &... # 288| getArgument(0): [Argument] : 5 # 288| getExpr(): [IntegerLiteralExpr] 5 # 288| getSource(): [BinaryExpr] ... .%(_:_:) ... @@ -1830,8 +1851,8 @@ cfg.swift: # 288| getMethodRef(): [DeclRefExpr] %(_:_:) # 288| getArgument(0): [Argument] : ...[...] # 288| getExpr(): [SubscriptExpr] ...[...] -# 288| getBase(): [InOutExpr] &... -# 288| getSubExpr(): [DeclRefExpr] b +# 288| getBase(): [DeclRefExpr] b +# 288| getBase().getFullyConverted(): [InOutExpr] &... # 288| getArgument(0): [Argument] : 4 # 288| getExpr(): [IntegerLiteralExpr] 4 # 288| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1839,8 +1860,8 @@ cfg.swift: # 288| getExpr(): [IntegerLiteralExpr] 1 # 289| getElement(20): [AssignExpr] ... = ... # 289| getDest(): [SubscriptExpr] ...[...] -# 289| getBase(): [InOutExpr] &... -# 289| getSubExpr(): [DeclRefExpr] b +# 289| getBase(): [DeclRefExpr] b +# 289| getBase().getFullyConverted(): [InOutExpr] &... # 289| getArgument(0): [Argument] : 6 # 289| getExpr(): [IntegerLiteralExpr] 6 # 289| getSource(): [BinaryExpr] ... .&(_:_:) ... @@ -1850,8 +1871,8 @@ cfg.swift: # 289| getMethodRef(): [DeclRefExpr] &(_:_:) # 289| getArgument(0): [Argument] : ...[...] # 289| getExpr(): [SubscriptExpr] ...[...] -# 289| getBase(): [InOutExpr] &... -# 289| getSubExpr(): [DeclRefExpr] b +# 289| getBase(): [DeclRefExpr] b +# 289| getBase().getFullyConverted(): [InOutExpr] &... # 289| getArgument(0): [Argument] : 5 # 289| getExpr(): [IntegerLiteralExpr] 5 # 289| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1859,8 +1880,8 @@ cfg.swift: # 289| getExpr(): [IntegerLiteralExpr] 1 # 290| getElement(21): [AssignExpr] ... = ... # 290| getDest(): [SubscriptExpr] ...[...] -# 290| getBase(): [InOutExpr] &... -# 290| getSubExpr(): [DeclRefExpr] b +# 290| getBase(): [DeclRefExpr] b +# 290| getBase().getFullyConverted(): [InOutExpr] &... # 290| getArgument(0): [Argument] : 7 # 290| getExpr(): [IntegerLiteralExpr] 7 # 290| getSource(): [BinaryExpr] ... .|(_:_:) ... @@ -1870,8 +1891,8 @@ cfg.swift: # 290| getMethodRef(): [DeclRefExpr] |(_:_:) # 290| getArgument(0): [Argument] : ...[...] # 290| getExpr(): [SubscriptExpr] ...[...] -# 290| getBase(): [InOutExpr] &... -# 290| getSubExpr(): [DeclRefExpr] b +# 290| getBase(): [DeclRefExpr] b +# 290| getBase().getFullyConverted(): [InOutExpr] &... # 290| getArgument(0): [Argument] : 6 # 290| getExpr(): [IntegerLiteralExpr] 6 # 290| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1879,8 +1900,8 @@ cfg.swift: # 290| getExpr(): [IntegerLiteralExpr] 1 # 291| getElement(22): [AssignExpr] ... = ... # 291| getDest(): [SubscriptExpr] ...[...] -# 291| getBase(): [InOutExpr] &... -# 291| getSubExpr(): [DeclRefExpr] b +# 291| getBase(): [DeclRefExpr] b +# 291| getBase().getFullyConverted(): [InOutExpr] &... # 291| getArgument(0): [Argument] : 8 # 291| getExpr(): [IntegerLiteralExpr] 8 # 291| getSource(): [BinaryExpr] ... .^(_:_:) ... @@ -1890,8 +1911,8 @@ cfg.swift: # 291| getMethodRef(): [DeclRefExpr] ^(_:_:) # 291| getArgument(0): [Argument] : ...[...] # 291| getExpr(): [SubscriptExpr] ...[...] -# 291| getBase(): [InOutExpr] &... -# 291| getSubExpr(): [DeclRefExpr] b +# 291| getBase(): [DeclRefExpr] b +# 291| getBase().getFullyConverted(): [InOutExpr] &... # 291| getArgument(0): [Argument] : 7 # 291| getExpr(): [IntegerLiteralExpr] 7 # 291| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1899,8 +1920,8 @@ cfg.swift: # 291| getExpr(): [IntegerLiteralExpr] 1 # 292| getElement(23): [AssignExpr] ... = ... # 292| getDest(): [SubscriptExpr] ...[...] -# 292| getBase(): [InOutExpr] &... -# 292| getSubExpr(): [DeclRefExpr] b +# 292| getBase(): [DeclRefExpr] b +# 292| getBase().getFullyConverted(): [InOutExpr] &... # 292| getArgument(0): [Argument] : 9 # 292| getExpr(): [IntegerLiteralExpr] 9 # 292| getSource(): [BinaryExpr] ... .<<(_:_:) ... @@ -1910,8 +1931,8 @@ cfg.swift: # 292| getMethodRef(): [DeclRefExpr] <<(_:_:) # 292| getArgument(0): [Argument] : ...[...] # 292| getExpr(): [SubscriptExpr] ...[...] -# 292| getBase(): [InOutExpr] &... -# 292| getSubExpr(): [DeclRefExpr] b +# 292| getBase(): [DeclRefExpr] b +# 292| getBase().getFullyConverted(): [InOutExpr] &... # 292| getArgument(0): [Argument] : 8 # 292| getExpr(): [IntegerLiteralExpr] 8 # 292| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1919,8 +1940,8 @@ cfg.swift: # 292| getExpr(): [IntegerLiteralExpr] 1 # 293| getElement(24): [AssignExpr] ... = ... # 293| getDest(): [SubscriptExpr] ...[...] -# 293| getBase(): [InOutExpr] &... -# 293| getSubExpr(): [DeclRefExpr] b +# 293| getBase(): [DeclRefExpr] b +# 293| getBase().getFullyConverted(): [InOutExpr] &... # 293| getArgument(0): [Argument] : 10 # 293| getExpr(): [IntegerLiteralExpr] 10 # 293| getSource(): [BinaryExpr] ... .>>(_:_:) ... @@ -1930,8 +1951,8 @@ cfg.swift: # 293| getMethodRef(): [DeclRefExpr] >>(_:_:) # 293| getArgument(0): [Argument] : ...[...] # 293| getExpr(): [SubscriptExpr] ...[...] -# 293| getBase(): [InOutExpr] &... -# 293| getSubExpr(): [DeclRefExpr] b +# 293| getBase(): [DeclRefExpr] b +# 293| getBase().getFullyConverted(): [InOutExpr] &... # 293| getArgument(0): [Argument] : 9 # 293| getExpr(): [IntegerLiteralExpr] 9 # 293| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1957,8 +1978,8 @@ cfg.swift: # 296| getExpr(): [DeclRefExpr] a1 # 296| getArgument(1): [Argument] : ...[...] # 296| getExpr(): [SubscriptExpr] ...[...] -# 296| getBase(): [InOutExpr] &... -# 296| getSubExpr(): [DeclRefExpr] b +# 296| getBase(): [DeclRefExpr] b +# 296| getBase().getFullyConverted(): [InOutExpr] &... # 296| getArgument(0): [Argument] : 0 # 296| getExpr(): [IntegerLiteralExpr] 0 # 296| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1971,8 +1992,8 @@ cfg.swift: # 296| getExpr(): [DeclRefExpr] a2 # 296| getArgument(1): [Argument] : ...[...] # 296| getExpr(): [SubscriptExpr] ...[...] -# 296| getBase(): [InOutExpr] &... -# 296| getSubExpr(): [DeclRefExpr] b +# 296| getBase(): [DeclRefExpr] b +# 296| getBase().getFullyConverted(): [InOutExpr] &... # 296| getArgument(0): [Argument] : 1 # 296| getExpr(): [IntegerLiteralExpr] 1 # 296| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1985,8 +2006,8 @@ cfg.swift: # 296| getExpr(): [DeclRefExpr] a3 # 296| getArgument(1): [Argument] : ...[...] # 296| getExpr(): [SubscriptExpr] ...[...] -# 296| getBase(): [InOutExpr] &... -# 296| getSubExpr(): [DeclRefExpr] b +# 296| getBase(): [DeclRefExpr] b +# 296| getBase().getFullyConverted(): [InOutExpr] &... # 296| getArgument(0): [Argument] : 2 # 296| getExpr(): [IntegerLiteralExpr] 2 # 296| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -1999,8 +2020,8 @@ cfg.swift: # 296| getExpr(): [DeclRefExpr] a4 # 296| getArgument(1): [Argument] : ...[...] # 296| getExpr(): [SubscriptExpr] ...[...] -# 296| getBase(): [InOutExpr] &... -# 296| getSubExpr(): [DeclRefExpr] b +# 296| getBase(): [DeclRefExpr] b +# 296| getBase().getFullyConverted(): [InOutExpr] &... # 296| getArgument(0): [Argument] : 3 # 296| getExpr(): [IntegerLiteralExpr] 3 # 296| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -2013,8 +2034,8 @@ cfg.swift: # 296| getExpr(): [DeclRefExpr] a5 # 296| getArgument(1): [Argument] : ...[...] # 296| getExpr(): [SubscriptExpr] ...[...] -# 296| getBase(): [InOutExpr] &... -# 296| getSubExpr(): [DeclRefExpr] b +# 296| getBase(): [DeclRefExpr] b +# 296| getBase().getFullyConverted(): [InOutExpr] &... # 296| getArgument(0): [Argument] : 4 # 296| getExpr(): [IntegerLiteralExpr] 4 # 296| getExpr().getFullyConverted(): [LoadExpr] (Int) ... @@ -2054,9 +2075,9 @@ cfg.swift: # 302| getBase(): [TypeExpr] Int.Type # 302| getTypeRepr(): [TypeRepr] Int # 302| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 302| getArgument(0): [Argument] : &... -# 302| getExpr(): [InOutExpr] &... -# 302| getSubExpr(): [DeclRefExpr] x +# 302| getArgument(0): [Argument] : x +# 302| getExpr(): [DeclRefExpr] x +# 302| getExpr().getFullyConverted(): [InOutExpr] &... # 302| getArgument(1): [Argument] : 1 # 302| getExpr(): [IntegerLiteralExpr] 1 # 306| [NamedFunction] loop2(x:) @@ -2095,9 +2116,9 @@ cfg.swift: # 309| getBase(): [TypeExpr] Int.Type # 309| getTypeRepr(): [TypeRepr] Int # 309| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 309| getArgument(0): [Argument] : &... -# 309| getExpr(): [InOutExpr] &... -# 309| getSubExpr(): [DeclRefExpr] x +# 309| getArgument(0): [Argument] : x +# 309| getExpr(): [DeclRefExpr] x +# 309| getExpr().getFullyConverted(): [InOutExpr] &... # 309| getArgument(1): [Argument] : 1 # 309| getExpr(): [IntegerLiteralExpr] 1 # 310| getElement(2): [IfStmt] if ... then { ... } else { ... } @@ -2202,9 +2223,9 @@ cfg.swift: # 325| getBase(): [TypeExpr] Int.Type # 325| getTypeRepr(): [TypeRepr] Int # 325| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 325| getArgument(0): [Argument] : &... -# 325| getExpr(): [InOutExpr] &... -# 325| getSubExpr(): [DeclRefExpr] x +# 325| getArgument(0): [Argument] : x +# 325| getExpr(): [DeclRefExpr] x +# 325| getExpr().getFullyConverted(): [InOutExpr] &... # 325| getArgument(1): [Argument] : 1 # 325| getExpr(): [IntegerLiteralExpr] 1 # 326| getElement(2): [IfStmt] if ... then { ... } else { ... } @@ -2293,9 +2314,9 @@ cfg.swift: # 341| getBase(): [TypeExpr] Int.Type # 341| getTypeRepr(): [TypeRepr] Int # 341| getMethodRef(): [DeclRefExpr] -=(_:_:) -# 341| getArgument(0): [Argument] : &... -# 341| getExpr(): [InOutExpr] &... -# 341| getSubExpr(): [DeclRefExpr] x +# 341| getArgument(0): [Argument] : x +# 341| getExpr(): [DeclRefExpr] x +# 341| getExpr().getFullyConverted(): [InOutExpr] &... # 341| getArgument(1): [Argument] : 1 # 341| getExpr(): [IntegerLiteralExpr] 1 # 345| [NamedFunction] loop_with_identity_expr() @@ -2326,9 +2347,9 @@ cfg.swift: # 348| getBase(): [TypeExpr] Int.Type # 348| getTypeRepr(): [TypeRepr] Int # 348| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 348| getArgument(0): [Argument] : &... -# 348| getExpr(): [InOutExpr] &... -# 348| getSubExpr(): [DeclRefExpr] x +# 348| getArgument(0): [Argument] : x +# 348| getExpr(): [DeclRefExpr] x +# 348| getExpr().getFullyConverted(): [InOutExpr] &... # 348| getArgument(1): [Argument] : 1 # 348| getExpr(): [IntegerLiteralExpr] 1 # 352| [ClassDecl] OptionalC @@ -2568,9 +2589,9 @@ cfg.swift: # 394| Type = Structors # 394| getBody(): [BraceStmt] { ... } # 394| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .field -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 395| getMember(2): [Initializer] Structors.init() # 395| InterfaceType = (Structors.Type) -> () -> Structors # 395| getSelfParam(): [ParamDecl] self @@ -2642,9 +2663,9 @@ cfg.swift: # 410| Type = MyLocalClass # 410| getBody(): [BraceStmt] { ... } # 410| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 411| getMember(2): [Initializer] MyLocalClass.init() # 411| InterfaceType = (MyLocalClass.Type) -> () -> MyLocalClass # 411| getSelfParam(): [ParamDecl] self @@ -2692,9 +2713,9 @@ cfg.swift: # 417| Type = MyLocalStruct # 417| getBody(): [BraceStmt] { ... } # 417| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 418| getMember(2): [Initializer] MyLocalStruct.init() # 418| InterfaceType = (MyLocalStruct.Type) -> () -> MyLocalStruct # 418| getSelfParam(): [ParamDecl] self @@ -2797,8 +2818,8 @@ cfg.swift: #-----| getPattern(): [EnumElementPattern] .B #-----| getElement(2): [CallExpr] call to combine(_:) #-----| getFunction(): [MethodLookupExpr] .combine(_:) -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getBase(): [DeclRefExpr] hasher +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator @@ -2873,9 +2894,9 @@ cfg.swift: # 446| Type = B # 446| getBody(): [BraceStmt] { ... } # 446| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 445| getMember(2): [Initializer] B.init(x:) # 445| InterfaceType = (B.Type) -> (Int) -> B # 445| getSelfParam(): [ParamDecl] self @@ -2914,9 +2935,9 @@ cfg.swift: # 450| Type = A # 450| getBody(): [BraceStmt] { ... } # 450| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .b -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 451| getMember(2): [PatternBindingDecl] var ... = ... # 451| getPattern(0): [TypedPattern] ... as ... # 451| getSubPattern(): [NamedPattern] bs @@ -2948,9 +2969,9 @@ cfg.swift: # 451| Type = A # 451| getBody(): [BraceStmt] { ... } # 451| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .bs -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 452| getMember(4): [PatternBindingDecl] var ... = ... #-----| getInit(0): [NilLiteralExpr] nil # 452| getPattern(0): [TypedPattern] ... as ... @@ -2983,9 +3004,9 @@ cfg.swift: # 452| Type = A # 452| getBody(): [BraceStmt] { ... } # 452| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .mayB -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 449| getMember(6): [Initializer] A.init(b:bs:mayB:) # 449| InterfaceType = (A.Type) -> (B, [B], B?) -> A # 449| getSelfParam(): [ParamDecl] self @@ -3104,9 +3125,9 @@ cfg.swift: # 500| getBase(): [TypeExpr] Int.Type # 500| getTypeRepr(): [TypeRepr] Int # 500| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 500| getArgument(0): [Argument] : &... -# 500| getExpr(): [InOutExpr] &... -# 500| getSubExpr(): [DeclRefExpr] x +# 500| getArgument(0): [Argument] : x +# 500| getExpr(): [DeclRefExpr] x +# 500| getExpr().getFullyConverted(): [InOutExpr] &... # 500| getArgument(1): [Argument] : 1 # 500| getExpr(): [IntegerLiteralExpr] 1 # 503| getElement(2): [IfStmt] if ... then { ... } @@ -3121,9 +3142,9 @@ cfg.swift: # 504| getBase(): [TypeExpr] Int.Type # 504| getTypeRepr(): [TypeRepr] Int # 504| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 504| getArgument(0): [Argument] : &... -# 504| getExpr(): [InOutExpr] &... -# 504| getSubExpr(): [DeclRefExpr] x +# 504| getArgument(0): [Argument] : x +# 504| getExpr(): [DeclRefExpr] x +# 504| getExpr().getFullyConverted(): [InOutExpr] &... # 504| getArgument(1): [Argument] : 1 # 504| getExpr(): [IntegerLiteralExpr] 1 # 507| getElement(3): [IfStmt] if ... then { ... } @@ -3139,9 +3160,9 @@ cfg.swift: # 508| getBase(): [TypeExpr] Int.Type # 508| getTypeRepr(): [TypeRepr] Int # 508| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 508| getArgument(0): [Argument] : &... -# 508| getExpr(): [InOutExpr] &... -# 508| getSubExpr(): [DeclRefExpr] x +# 508| getArgument(0): [Argument] : x +# 508| getExpr(): [DeclRefExpr] x +# 508| getExpr().getFullyConverted(): [InOutExpr] &... # 508| getArgument(1): [Argument] : 1 # 508| getExpr(): [IntegerLiteralExpr] 1 # 511| getElement(4): [GuardStmt] guard ... else { ... } @@ -3156,9 +3177,9 @@ cfg.swift: # 512| getBase(): [TypeExpr] Int.Type # 512| getTypeRepr(): [TypeRepr] Int # 512| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 512| getArgument(0): [Argument] : &... -# 512| getExpr(): [InOutExpr] &... -# 512| getSubExpr(): [DeclRefExpr] x +# 512| getArgument(0): [Argument] : x +# 512| getExpr(): [DeclRefExpr] x +# 512| getExpr().getFullyConverted(): [InOutExpr] &... # 512| getArgument(1): [Argument] : 1 # 512| getExpr(): [IntegerLiteralExpr] 1 # 515| getElement(5): [IfStmt] if ... then { ... } @@ -3177,9 +3198,9 @@ cfg.swift: # 516| getBase(): [TypeExpr] Int.Type # 516| getTypeRepr(): [TypeRepr] Int # 516| getMethodRef(): [DeclRefExpr] +=(_:_:) -# 516| getArgument(0): [Argument] : &... -# 516| getExpr(): [InOutExpr] &... -# 516| getSubExpr(): [DeclRefExpr] x +# 516| getArgument(0): [Argument] : x +# 516| getExpr(): [DeclRefExpr] x +# 516| getExpr().getFullyConverted(): [InOutExpr] &... # 516| getArgument(1): [Argument] : 1 # 516| getExpr(): [IntegerLiteralExpr] 1 # 519| getElement(6): [ReturnStmt] return ... @@ -3217,9 +3238,9 @@ declarations.swift: # 2| Type = Foo # 2| getBody(): [BraceStmt] { ... } # 2| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 3| getMember(2): [PatternBindingDecl] var ... = ... # 3| getPattern(0): [TypedPattern] ... as ... # 3| getSubPattern(): [NamedPattern] next @@ -3267,9 +3288,9 @@ declarations.swift: # 3| Type = Foo # 3| getBody(): [BraceStmt] { ... } # 3| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .next -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .next +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 1| getMember(4): [Initializer] Foo.init() # 1| InterfaceType = (Foo.Type) -> () -> Foo # 1| getSelfParam(): [ParamDecl] self @@ -3315,9 +3336,9 @@ declarations.swift: # 9| Type = Bar # 9| getBody(): [BraceStmt] { ... } # 9| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 9| getMember(2): [Deinitializer] Bar.deinit() # 9| InterfaceType = (Bar) -> () -> () # 9| getSelfParam(): [ParamDecl] self @@ -3487,8 +3508,8 @@ declarations.swift: #-----| getPattern(): [EnumElementPattern] .value5 #-----| getElement(2): [CallExpr] call to combine(_:) #-----| getFunction(): [MethodLookupExpr] .combine(_:) -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getBase(): [DeclRefExpr] hasher +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator @@ -3633,9 +3654,9 @@ declarations.swift: # 41| Type = Baz # 41| getBody(): [BraceStmt] { ... } # 41| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .field -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .field +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 42| getMember(2): [Initializer] Baz.init() # 42| InterfaceType = (Baz.Type) -> () -> Baz # 42| getSelfParam(): [ParamDecl] self @@ -3743,9 +3764,9 @@ declarations.swift: # 82| Type = HasPropertyAndObserver # 82| getBody(): [BraceStmt] { ... } # 82| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .settableField -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .settableField +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 91| getMember(2): [PatternBindingDecl] var ... = ... # 91| getPattern(0): [TypedPattern] ... as ... # 91| getSubPattern(): [NamedPattern] readOnlyField1 @@ -3803,9 +3824,9 @@ declarations.swift: # 102| Type = HasPropertyAndObserver # 102| getBody(): [BraceStmt] { ... } # 102| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .normalField -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 104| getMember(8): [SubscriptDecl] subscript ... # 105| getAccessor(0): [Accessor] get # 105| InterfaceType = (HasPropertyAndObserver) -> (Int) -> Int @@ -3833,11 +3854,11 @@ declarations.swift: #-----| Type = Int # 104| getBody(): [BraceStmt] { ... } # 104| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [SubscriptExpr] ...[...] -#-----| getBase(): [DeclRefExpr] self -#-----| getArgument(0): [Argument] : x -#-----| getExpr(): [DeclRefExpr] x +#-----| getResult(0): [SubscriptExpr] ...[...] +#-----| getBase(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : x +#-----| getExpr(): [DeclRefExpr] x +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 104| getParam(0): [ParamDecl] x # 104| Type = Int # 111| getMember(9): [SubscriptDecl] subscript ... @@ -3886,8 +3907,8 @@ declarations.swift: # 115| getBody(): [BraceStmt] { ... } #-----| getElement(0): [CallExpr] call to willSet #-----| getFunction(): [MethodLookupExpr] .willSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] willSet #-----| getArgument(0): [Argument] : value #-----| getExpr(): [DeclRefExpr] value @@ -3901,9 +3922,9 @@ declarations.swift: # 115| Type = HasPropertyAndObserver # 115| getBody(): [BraceStmt] { ... } # 115| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .hasWillSet1 -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .hasWillSet1 +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 119| getMember(12): [PatternBindingDecl] var ... = ... # 119| getPattern(0): [TypedPattern] ... as ... # 119| getSubPattern(): [NamedPattern] hasWillSet2 @@ -3934,8 +3955,8 @@ declarations.swift: # 119| getBody(): [BraceStmt] { ... } #-----| getElement(0): [CallExpr] call to willSet #-----| getFunction(): [MethodLookupExpr] .willSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] willSet #-----| getArgument(0): [Argument] : value #-----| getExpr(): [DeclRefExpr] value @@ -3949,9 +3970,9 @@ declarations.swift: # 119| Type = HasPropertyAndObserver # 119| getBody(): [BraceStmt] { ... } # 119| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .hasWillSet2 -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .hasWillSet2 +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 123| getMember(14): [PatternBindingDecl] var ... = ... # 123| getPattern(0): [TypedPattern] ... as ... # 123| getSubPattern(): [NamedPattern] hasDidSet1 @@ -3993,8 +4014,8 @@ declarations.swift: #-----| getSource(): [DeclRefExpr] value #-----| getElement(2): [CallExpr] call to didSet #-----| getFunction(): [MethodLookupExpr] .didSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] didSet #-----| getArgument(0): [Argument] : tmp #-----| getExpr(): [DeclRefExpr] tmp @@ -4004,9 +4025,9 @@ declarations.swift: # 123| Type = HasPropertyAndObserver # 123| getBody(): [BraceStmt] { ... } # 123| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .hasDidSet1 -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .hasDidSet1 +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 127| getMember(16): [PatternBindingDecl] var ... = ... # 127| getPattern(0): [TypedPattern] ... as ... # 127| getSubPattern(): [NamedPattern] hasDidSet2 @@ -4039,8 +4060,8 @@ declarations.swift: #-----| getSource(): [DeclRefExpr] value #-----| getElement(1): [CallExpr] call to didSet #-----| getFunction(): [MethodLookupExpr] .didSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] didSet # 127| getAccessor(3): [Accessor] _modify # 127| InterfaceType = (inout HasPropertyAndObserver) -> () -> () @@ -4048,13 +4069,13 @@ declarations.swift: # 127| Type = HasPropertyAndObserver # 127| getBody(): [BraceStmt] { ... } # 127| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .hasDidSet2 -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .hasDidSet2 +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... #-----| getElement(1): [CallExpr] call to didSet #-----| getFunction(): [MethodLookupExpr] .didSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] didSet # 131| getMember(18): [PatternBindingDecl] var ... = ... # 131| getPattern(0): [TypedPattern] ... as ... @@ -4091,8 +4112,8 @@ declarations.swift: # 131| getBody(): [BraceStmt] { ... } #-----| getElement(0): [CallExpr] call to willSet #-----| getFunction(): [MethodLookupExpr] .willSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] willSet #-----| getArgument(0): [Argument] : value #-----| getExpr(): [DeclRefExpr] value @@ -4102,8 +4123,8 @@ declarations.swift: #-----| getSource(): [DeclRefExpr] value #-----| getElement(2): [CallExpr] call to didSet #-----| getFunction(): [MethodLookupExpr] .didSet -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] self +#-----| getBase(): [DeclRefExpr] self +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] didSet # 131| getAccessor(4): [Accessor] _modify # 131| InterfaceType = (inout HasPropertyAndObserver) -> () -> () @@ -4111,9 +4132,9 @@ declarations.swift: # 131| Type = HasPropertyAndObserver # 131| getBody(): [BraceStmt] { ... } # 131| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .hasBoth -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .hasBoth +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 81| getMember(20): [Initializer] HasPropertyAndObserver.init(normalField:hasWillSet1:hasWillSet2:hasDidSet1:hasDidSet2:hasBoth:) # 81| InterfaceType = (HasPropertyAndObserver.Type) -> (Int, Int, Int, Int, Int, Int) -> HasPropertyAndObserver # 81| getSelfParam(): [ParamDecl] self @@ -4302,22 +4323,22 @@ expressions.swift: #-----| Type = DefaultStringInterpolation # 7| getElement(0): [CallExpr] call to appendLiteral(_:) # 7| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 7| getBase(): [InOutExpr] &... -# 7| getSubExpr(): [DeclRefExpr] $interpolation +# 7| getBase(): [DeclRefExpr] $interpolation +# 7| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 7| getArgument(0): [Argument] : hello # 7| getExpr(): [StringLiteralExpr] hello # 7| getElement(1): [CallExpr] call to appendInterpolation(_:) # 7| getFunction(): [MethodLookupExpr] .appendInterpolation(_:) -# 7| getBase(): [InOutExpr] &... -# 7| getSubExpr(): [DeclRefExpr] $interpolation +# 7| getBase(): [DeclRefExpr] $interpolation +# 7| getBase().getFullyConverted(): [InOutExpr] &... # 7| getMethodRef(): [DeclRefExpr] appendInterpolation(_:) # 7| getArgument(0): [Argument] : a # 7| getExpr(): [DeclRefExpr] a # 7| getElement(2): [CallExpr] call to appendLiteral(_:) # 7| getFunction(): [MethodLookupExpr] .appendLiteral(_:) -# 7| getBase(): [InOutExpr] &... -# 7| getSubExpr(): [DeclRefExpr] $interpolation +# 7| getBase(): [DeclRefExpr] $interpolation +# 7| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] appendLiteral(_:) # 7| getArgument(0): [Argument] : # 7| getExpr(): [StringLiteralExpr] @@ -4402,8 +4423,8 @@ expressions.swift: #-----| getPattern(): [EnumElementPattern] .failed #-----| getElement(2): [CallExpr] call to combine(_:) #-----| getFunction(): [MethodLookupExpr] .combine(_:) -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getBase(): [DeclRefExpr] hasher +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator @@ -4676,10 +4697,10 @@ expressions.swift: # 59| getBody(): [BraceStmt] { ... } # 59| getElement(0): [CallExpr] call to unsafeFunction(pointer:) # 59| getFunction(): [DeclRefExpr] unsafeFunction(pointer:) -# 59| getArgument(0): [Argument] pointer: &... -# 59| getExpr(): [InOutExpr] &... -# 59| getSubExpr(): [DeclRefExpr] myNumber +# 59| getArgument(0): [Argument] pointer: myNumber +# 59| getExpr(): [DeclRefExpr] myNumber # 59| getExpr().getFullyConverted(): [InOutToPointerExpr] (UnsafePointer) ... +# 59| getSubExpr(): [InOutExpr] &... # 60| [TopLevelCodeDecl] { ... } # 60| getBody(): [BraceStmt] { ... } # 60| getElement(0): [CallExpr] call to withUnsafePointer(to:_:) @@ -4904,9 +4925,9 @@ expressions.swift: # 96| Type = HasProperty # 96| getBody(): [BraceStmt] { ... } # 96| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .settableField -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .settableField +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 105| getMember(2): [PatternBindingDecl] var ... = ... # 105| getPattern(0): [TypedPattern] ... as ... # 105| getSubPattern(): [NamedPattern] readOnlyField1 @@ -4964,9 +4985,9 @@ expressions.swift: # 116| Type = HasProperty # 116| getBody(): [BraceStmt] { ... } # 116| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .normalField -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .normalField +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 118| getMember(8): [SubscriptDecl] subscript ... # 119| getAccessor(0): [Accessor] get # 119| InterfaceType = (HasProperty) -> (Int) -> Int @@ -4994,11 +5015,11 @@ expressions.swift: #-----| Type = Int # 118| getBody(): [BraceStmt] { ... } # 118| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [SubscriptExpr] ...[...] -#-----| getBase(): [DeclRefExpr] self -#-----| getArgument(0): [Argument] : x -#-----| getExpr(): [DeclRefExpr] x +#-----| getResult(0): [SubscriptExpr] ...[...] +#-----| getBase(): [DeclRefExpr] self +#-----| getArgument(0): [Argument] : x +#-----| getExpr(): [DeclRefExpr] x +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 118| getParam(0): [ParamDecl] x # 118| Type = Int # 125| getMember(9): [SubscriptDecl] subscript ... @@ -5072,8 +5093,8 @@ expressions.swift: # 136| getPattern(0): [NamedPattern] w # 137| getElement(6): [AssignExpr] ... = ... # 137| getDest(): [SubscriptExpr] ...[...] -# 137| getBase(): [InOutExpr] &... -# 137| getSubExpr(): [DeclRefExpr] hp +# 137| getBase(): [DeclRefExpr] hp +# 137| getBase().getFullyConverted(): [InOutExpr] &... # 137| getArgument(0): [Argument] : 1 # 137| getExpr(): [IntegerLiteralExpr] 1 # 137| getSource(): [IntegerLiteralExpr] 2 @@ -5117,9 +5138,9 @@ expressions.swift: # 142| Type = B # 142| getBody(): [BraceStmt] { ... } # 142| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 141| getMember(2): [Initializer] B.init(x:) # 141| InterfaceType = (B.Type) -> (Int) -> B # 141| getSelfParam(): [ParamDecl] self @@ -5158,9 +5179,9 @@ expressions.swift: # 146| Type = A # 146| getBody(): [BraceStmt] { ... } # 146| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .b -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .b +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 147| getMember(2): [PatternBindingDecl] var ... = ... # 147| getPattern(0): [TypedPattern] ... as ... # 147| getSubPattern(): [NamedPattern] bs @@ -5192,9 +5213,9 @@ expressions.swift: # 147| Type = A # 147| getBody(): [BraceStmt] { ... } # 147| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .bs -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .bs +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 148| getMember(4): [PatternBindingDecl] var ... = ... #-----| getInit(0): [NilLiteralExpr] nil # 148| getPattern(0): [TypedPattern] ... as ... @@ -5227,9 +5248,9 @@ expressions.swift: # 148| Type = A # 148| getBody(): [BraceStmt] { ... } # 148| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .mayB -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .mayB +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 145| getMember(6): [Initializer] A.init(b:bs:mayB:) # 145| InterfaceType = (A.Type) -> (B, [B], B?) -> A # 145| getSelfParam(): [ParamDecl] self @@ -5373,9 +5394,9 @@ expressions.swift: # 167| Type = Bar # 167| getBody(): [BraceStmt] { ... } # 167| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .value -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .value +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 168| getMember(2): [PatternBindingDecl] var ... = ... #-----| getInit(0): [NilLiteralExpr] nil # 168| getPattern(0): [TypedPattern] ... as ... @@ -5408,9 +5429,9 @@ expressions.swift: # 168| Type = Bar # 168| getBody(): [BraceStmt] { ... } # 168| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .opt -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .opt +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 166| getMember(4): [Initializer] Bar.init(value:opt:) # 166| InterfaceType = (Bar.Type) -> (Int, Int?) -> Bar # 166| getSelfParam(): [ParamDecl] self @@ -5451,9 +5472,9 @@ expressions.swift: # 172| Type = Foo # 172| getBody(): [BraceStmt] { ... } # 172| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .value -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .value +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 173| getMember(2): [PatternBindingDecl] var ... = ... #-----| getInit(0): [NilLiteralExpr] nil # 173| getPattern(0): [TypedPattern] ... as ... @@ -5486,9 +5507,9 @@ expressions.swift: # 173| Type = Foo # 173| getBody(): [BraceStmt] { ... } # 173| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .opt -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .opt +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 171| getMember(4): [Initializer] Foo.init(value:opt:) # 171| InterfaceType = (Foo.Type) -> (Int, Bar?) -> Foo # 171| getSelfParam(): [ParamDecl] self @@ -5652,10 +5673,20 @@ patterns.swift: # 16| getElement(0): [StringLiteralExpr] expr # 16| getLabel(0): [CaseLabelItem] =~ ... # 16| getPattern(): [ExprPattern] =~ ... -# 16| getSubExpr(): [SequenceExpr] SequenceExpr -# 16| getElement(0): [IntegerLiteralExpr] 1 -# 16| getElement(1): [OverloadedDeclRefExpr] OverloadedDeclRefExpr -# 16| getElement(2): [IntegerLiteralExpr] 2 +# 16| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 16| getFunction(): [DeclRefExpr] ~=(_:_:) +# 16| getArgument(0): [Argument] : ... .+(_:_:) ... +# 16| getExpr(): [BinaryExpr] ... .+(_:_:) ... +# 16| getFunction(): [MethodLookupExpr] .+(_:_:) +# 16| getBase(): [TypeExpr] Int.Type +# 16| getTypeRepr(): [TypeRepr] Int +# 16| getMethodRef(): [DeclRefExpr] +(_:_:) +# 16| getArgument(0): [Argument] : 1 +# 16| getExpr(): [IntegerLiteralExpr] 1 +# 16| getArgument(1): [Argument] : 2 +# 16| getExpr(): [IntegerLiteralExpr] 2 +# 16| getArgument(1): [Argument] : $match +# 16| getExpr(): [DeclRefExpr] $match # 17| getCase(1): [CaseStmt] case ... # 17| getBody(): [BraceStmt] { ... } # 17| getElement(0): [StringLiteralExpr] @@ -5758,6 +5789,8 @@ patterns.swift: # 12| Type = Int # 12| [ConcreteVarDecl] yy # 12| Type = Int +# 16| [ConcreteVarDecl] $match +# 16| Type = Int # 28| [ConcreteVarDecl] i # 28| Type = Int # 28| [ConcreteVarDecl] s @@ -5814,7 +5847,12 @@ patterns.swift: # 58| getElement(0): [ConditionElement] (...) = ... # 58| getPattern(): [TuplePattern] (...) # 58| getElement(0): [ExprPattern] =~ ... -# 58| getSubExpr(): [DeclRefExpr] a +# 58| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 58| getFunction(): [DeclRefExpr] ~=(_:_:) +# 58| getArgument(0): [Argument] : a +# 58| getExpr(): [DeclRefExpr] a +# 58| getArgument(1): [Argument] : $match +# 58| getExpr(): [DeclRefExpr] $match # 58| getElement(1): [NamedPattern] b # 58| getElement(1).getFullyUnresolved(): [BindingPattern] let ... # 58| getElement(2): [NamedPattern] c @@ -5835,7 +5873,12 @@ patterns.swift: # 61| getElement(0): [StringLiteralExpr] equals c # 61| getLabel(0): [CaseLabelItem] =~ ... # 61| getPattern(): [ExprPattern] =~ ... -# 61| getSubExpr(): [DeclRefExpr] c +# 61| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 61| getFunction(): [DeclRefExpr] ~=(_:_:) +# 61| getArgument(0): [Argument] : c +# 61| getExpr(): [DeclRefExpr] c +# 61| getArgument(1): [Argument] : $match +# 61| getExpr(): [DeclRefExpr] $match # 62| getCase(1): [CaseStmt] case ... # 62| getBody(): [BraceStmt] { ... } # 62| getElement(0): [StringLiteralExpr] binds c @@ -5851,8 +5894,12 @@ patterns.swift: # 57| Type = Int # 57| [ConcreteVarDecl] c # 57| Type = Int +# 58| [ConcreteVarDecl] $match +# 58| Type = Int # 58| [ConcreteVarDecl] b # 58| Type = Int +# 61| [ConcreteVarDecl] $match +# 61| Type = Int # 62| [ConcreteVarDecl] c # 62| Type = Int # 67| [NamedFunction] source() @@ -6667,8 +6714,8 @@ statements.swift: #-----| getPattern(): [EnumElementPattern] .failed #-----| getElement(2): [CallExpr] call to combine(_:) #-----| getFunction(): [MethodLookupExpr] .combine(_:) -#-----| getBase(): [InOutExpr] &... -#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getBase(): [DeclRefExpr] hasher +#-----| getBase().getFullyConverted(): [InOutExpr] &... #-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator @@ -6766,7 +6813,12 @@ statements.swift: # 56| getElement(1): [FallthroughStmt] fallthrough # 54| getLabel(0): [CaseLabelItem] =~ ... # 54| getPattern(): [ExprPattern] =~ ... -# 54| getSubExpr(): [IntegerLiteralExpr] 1 +# 54| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 54| getFunction(): [DeclRefExpr] ~=(_:_:) +# 54| getArgument(0): [Argument] : 1 +# 54| getExpr(): [IntegerLiteralExpr] 1 +# 54| getArgument(1): [Argument] : $match +# 54| getExpr(): [DeclRefExpr] $match # 57| getCase(1): [CaseStmt] case ... # 58| getBody(): [BraceStmt] { ... } # 58| getElement(0): [CallExpr] call to print(_:separator:terminator:) @@ -6783,10 +6835,20 @@ statements.swift: # 59| getElement(1): [BreakStmt] break # 57| getLabel(0): [CaseLabelItem] =~ ... # 57| getPattern(): [ExprPattern] =~ ... -# 57| getSubExpr(): [IntegerLiteralExpr] 5 +# 57| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 57| getFunction(): [DeclRefExpr] ~=(_:_:) +# 57| getArgument(0): [Argument] : 5 +# 57| getExpr(): [IntegerLiteralExpr] 5 +# 57| getArgument(1): [Argument] : $match +# 57| getExpr(): [DeclRefExpr] $match # 57| getLabel(1): [CaseLabelItem] =~ ... # 57| getPattern(): [ExprPattern] =~ ... -# 57| getSubExpr(): [IntegerLiteralExpr] 10 +# 57| getSubExpr(): [BinaryExpr] ... ~=(_:_:) ... +# 57| getFunction(): [DeclRefExpr] ~=(_:_:) +# 57| getArgument(0): [Argument] : 10 +# 57| getExpr(): [IntegerLiteralExpr] 10 +# 57| getArgument(1): [Argument] : $match +# 57| getExpr(): [DeclRefExpr] $match # 60| getCase(2): [CaseStmt] case ... # 61| getBody(): [BraceStmt] { ... } # 61| getElement(0): [CallExpr] call to print(_:separator:terminator:) @@ -6802,6 +6864,12 @@ statements.swift: # 61| getExpr(): [DefaultArgumentExpr] default terminator # 60| getLabel(0): [CaseLabelItem] _ # 60| getPattern(): [AnyPattern] _ +# 54| [ConcreteVarDecl] $match +# 54| Type = Int +# 57| [ConcreteVarDecl] $match +# 57| Type = Int +# 57| [ConcreteVarDecl] $match +# 57| Type = Int # 64| [TopLevelCodeDecl] { ... } # 64| getBody(): [BraceStmt] { ... } # 64| getElement(0): [PatternBindingDecl] var ... = ... @@ -6899,9 +6967,9 @@ statements.swift: # 75| Type = HasModifyAccessorDecl # 75| getBody(): [BraceStmt] { ... } # 75| getElement(0): [YieldStmt] yield ... -#-----| getResult(0): [InOutExpr] &... -#-----| getSubExpr(): [MemberRefExpr] .x -#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0): [MemberRefExpr] .x +#-----| getBase(): [DeclRefExpr] self +#-----| getResult(0).getFullyConverted(): [InOutExpr] &... # 76| getMember(2): [PatternBindingDecl] var ... = ... # 76| getPattern(0): [TypedPattern] ... as ... # 76| getSubPattern(): [NamedPattern] hasModify @@ -6914,9 +6982,9 @@ statements.swift: # 77| Type = HasModifyAccessorDecl # 77| getBody(): [BraceStmt] { ... } # 78| getElement(0): [YieldStmt] yield ... -# 78| getResult(0): [InOutExpr] &... -# 78| getSubExpr(): [MemberRefExpr] .x -# 78| getBase(): [DeclRefExpr] self +# 78| getResult(0): [MemberRefExpr] .x +# 78| getBase(): [DeclRefExpr] self +# 78| getResult(0).getFullyConverted(): [InOutExpr] &... # 81| getAccessor(1): [Accessor] get # 81| InterfaceType = (HasModifyAccessorDecl) -> () -> Int # 81| getSelfParam(): [ParamDecl] self diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 067f4c42519..4f87ff785ed 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1458,26 +1458,44 @@ cfg.swift: # 141| case ... #-----| -> =~ ... -# 141| 0 +# 141| $match +#-----| -> ... ~=(_:_:) ... + +# 141| ... ~=(_:_:) ... #-----| -> =~ ... +# 141| 0 +#-----| -> $match + # 141| =~ ... -#-----| -> 0 +#-----| -> ~=(_:_:) # 141| =~ ... #-----| no-match -> =~ ... #-----| match -> true -# 141| 1 +# 141| ~=(_:_:) +#-----| -> 0 + +# 141| $match +#-----| -> ... ~=(_:_:) ... + +# 141| ... ~=(_:_:) ... #-----| -> =~ ... +# 141| 1 +#-----| -> $match + # 141| =~ ... -#-----| -> 1 +#-----| -> ~=(_:_:) # 141| =~ ... #-----| match -> true #-----| no-match -> case ... +# 141| ~=(_:_:) +#-----| -> 1 + # 142| return ... #-----| return -> exit patterns(x:) (normal) @@ -1487,15 +1505,24 @@ cfg.swift: # 144| case ... #-----| -> =~ ... where ... +# 144| $match +#-----| -> ... ~=(_:_:) ... + +# 144| ... ~=(_:_:) ... +#-----| -> =~ ... + # 144| =~ ... #-----| match, no-match -> ... .&&(_:_:) ... #-----| no-match -> case ... # 144| x -#-----| -> =~ ... +#-----| -> $match + +# 144| ~=(_:_:) +#-----| -> x # 144| =~ ... where ... -#-----| -> x +#-----| -> ~=(_:_:) # 144| ... .&&(_:_:) ... diff --git a/swift/ql/test/library-tests/dataflow/dataflow/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/dataflow/dataflow/CONSISTENCY/CfgConsistency.expected index 500c114c6c9..53bfb69f0dd 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/CONSISTENCY/CfgConsistency.expected @@ -1,3 +1,3 @@ multipleSuccessors -| test.swift:488:8:488:12 | let ...? | no-match | test.swift:488:27:488:27 | y | -| test.swift:488:8:488:12 | let ...? | no-match | test.swift:493:9:493:9 | tuple1 | +| test.swift:519:8:519:12 | let ...? | no-match | test.swift:519:27:519:27 | y | +| test.swift:519:8:519:12 | let ...? | no-match | test.swift:524:9:524:9 | tuple1 | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index fe3fa95e7e9..bab6db77c24 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -14,22 +14,22 @@ edges | test.swift:29:26:29:29 | y | test.swift:31:15:31:15 | y | | test.swift:35:12:35:19 | call to source() | test.swift:39:15:39:29 | call to callee_source() | | test.swift:43:19:43:26 | call to source() | test.swift:50:15:50:15 | t | -| test.swift:53:1:56:1 | arg[return] | test.swift:61:22:61:23 | [post] &... | +| test.swift:53:1:56:1 | arg[return] | test.swift:61:23:61:23 | [post] x | | test.swift:54:11:54:18 | call to source() | test.swift:53:1:56:1 | arg[return] | -| test.swift:61:22:61:23 | [post] &... | test.swift:62:15:62:15 | x | +| test.swift:61:23:61:23 | [post] x | test.swift:62:15:62:15 | x | | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | -| test.swift:73:18:73:25 | call to source() | test.swift:75:21:75:22 | &... | +| test.swift:73:18:73:25 | call to source() | test.swift:75:22:75:22 | x | | test.swift:73:18:73:25 | call to source() | test.swift:76:15:76:15 | x | -| test.swift:75:21:75:22 | &... | test.swift:65:16:65:28 | arg1 | -| test.swift:75:21:75:22 | &... | test.swift:75:31:75:32 | [post] &... | -| test.swift:75:31:75:32 | [post] &... | test.swift:77:15:77:15 | y | -| test.swift:80:1:82:1 | arg[return] | test.swift:97:39:97:40 | [post] &... | +| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | +| test.swift:75:22:75:22 | x | test.swift:75:32:75:32 | [post] y | +| test.swift:75:32:75:32 | [post] y | test.swift:77:15:77:15 | y | +| test.swift:80:1:82:1 | arg[return] | test.swift:97:40:97:40 | [post] x | | test.swift:81:11:81:18 | call to source() | test.swift:80:1:82:1 | arg[return] | -| test.swift:84:1:91:1 | arg[return] | test.swift:104:40:104:41 | [post] &... | +| test.swift:84:1:91:1 | arg[return] | test.swift:104:41:104:41 | [post] x | | test.swift:86:15:86:22 | call to source() | test.swift:84:1:91:1 | arg[return] | | test.swift:89:15:89:22 | call to source() | test.swift:84:1:91:1 | arg[return] | -| test.swift:97:39:97:40 | [post] &... | test.swift:98:19:98:19 | x | -| test.swift:104:40:104:41 | [post] &... | test.swift:105:19:105:19 | x | +| test.swift:97:40:97:40 | [post] x | test.swift:98:19:98:19 | x | +| test.swift:104:41:104:41 | [post] x | test.swift:105:19:105:19 | x | | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | | test.swift:113:14:113:19 | arg | test.swift:114:19:114:19 | arg | | test.swift:113:14:113:19 | arg | test.swift:114:19:114:19 | arg | @@ -104,8 +104,8 @@ edges | test.swift:259:12:259:19 | call to source() | test.swift:259:12:259:19 | call to source() [some:0] | | test.swift:259:12:259:19 | call to source() | test.swift:263:13:263:28 | call to optionalSource() | | test.swift:259:12:259:19 | call to source() [some:0] | test.swift:263:13:263:28 | call to optionalSource() [some:0] | -| test.swift:259:12:259:19 | call to source() [some:0] | test.swift:486:13:486:28 | call to optionalSource() [some:0] | -| test.swift:259:12:259:19 | call to source() [some:0] | test.swift:513:13:513:28 | call to optionalSource() [some:0] | +| test.swift:259:12:259:19 | call to source() [some:0] | test.swift:517:13:517:28 | call to optionalSource() [some:0] | +| test.swift:259:12:259:19 | call to source() [some:0] | test.swift:544:13:544:28 | call to optionalSource() [some:0] | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:265:15:265:15 | x | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:267:15:267:16 | ...! | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:271:15:271:16 | ...? | @@ -115,14 +115,23 @@ edges | test.swift:263:13:263:28 | call to optionalSource() | test.swift:280:15:280:38 | ... ? ... : ... | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:291:16:291:17 | ...? | | test.swift:263:13:263:28 | call to optionalSource() | test.swift:303:15:303:16 | ...! | +| test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:267:15:267:15 | x [some:0] | +| test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:279:26:279:26 | x [some:0] | +| test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:280:26:280:26 | x [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:284:8:284:12 | let ...? [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:291:16:291:17 | ...? [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:298:11:298:15 | let ...? [some:0] | +| test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:303:15:303:15 | x [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:306:13:306:24 | .some(...) [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:314:10:314:21 | .some(...) [some:0] | +| test.swift:267:15:267:15 | x [some:0] | test.swift:267:15:267:16 | ...! | | test.swift:270:15:270:22 | call to source() | test.swift:270:15:270:31 | call to signum() | | test.swift:271:15:271:16 | ...? | test.swift:271:15:271:25 | call to signum() | | test.swift:271:15:271:25 | call to signum() | test.swift:271:15:271:25 | OptionalEvaluationExpr | +| test.swift:279:26:279:26 | x [some:0] | test.swift:279:26:279:27 | ...! | +| test.swift:279:26:279:27 | ...! | test.swift:279:15:279:31 | ... ? ... : ... | +| test.swift:280:26:280:26 | x [some:0] | test.swift:280:26:280:27 | ...! | +| test.swift:280:26:280:27 | ...! | test.swift:280:15:280:38 | ... ? ... : ... | | test.swift:280:31:280:38 | call to source() | test.swift:280:15:280:38 | ... ? ... : ... | | test.swift:282:31:282:38 | call to source() | test.swift:282:15:282:38 | ... ? ... : ... | | test.swift:284:8:284:12 | let ...? [some:0] | test.swift:284:12:284:12 | z | @@ -135,6 +144,7 @@ edges | test.swift:291:16:291:26 | call to signum() [some:0] | test.swift:291:8:291:12 | let ...? [some:0] | | test.swift:298:11:298:15 | let ...? [some:0] | test.swift:298:15:298:15 | z1 | | test.swift:298:15:298:15 | z1 | test.swift:300:15:300:15 | z1 | +| test.swift:303:15:303:15 | x [some:0] | test.swift:303:15:303:16 | ...! | | test.swift:303:15:303:16 | ...! | test.swift:303:15:303:25 | call to signum() | | test.swift:306:13:306:24 | .some(...) [some:0] | test.swift:306:23:306:23 | z | | test.swift:306:23:306:23 | z | test.swift:307:19:307:19 | z | @@ -162,118 +172,170 @@ edges | test.swift:357:15:357:15 | t1 [Tuple element at index 1] | test.swift:357:15:357:18 | .1 | | test.swift:360:15:360:15 | t2 [Tuple element at index 0] | test.swift:360:15:360:18 | .0 | | test.swift:361:15:361:15 | t2 [Tuple element at index 1] | test.swift:361:15:361:18 | .1 | -| test.swift:398:9:398:27 | call to ... [mySingle:0] | test.swift:403:10:403:25 | .mySingle(...) [mySingle:0] | -| test.swift:398:9:398:27 | call to ... [mySingle:0] | test.swift:412:13:412:28 | .mySingle(...) [mySingle:0] | -| test.swift:398:19:398:26 | call to source() | test.swift:398:9:398:27 | call to ... [mySingle:0] | -| test.swift:403:10:403:25 | .mySingle(...) [mySingle:0] | test.swift:403:24:403:24 | a | -| test.swift:403:24:403:24 | a | test.swift:404:19:404:19 | a | -| test.swift:412:13:412:28 | .mySingle(...) [mySingle:0] | test.swift:412:27:412:27 | x | -| test.swift:412:27:412:27 | x | test.swift:413:19:413:19 | x | -| test.swift:420:9:420:34 | call to ... [myPair:1] | test.swift:427:10:427:30 | .myPair(...) [myPair:1] | -| test.swift:420:9:420:34 | call to ... [myPair:1] | test.swift:437:13:437:33 | .myPair(...) [myPair:1] | -| test.swift:420:9:420:34 | call to ... [myPair:1] | test.swift:442:33:442:33 | a [myPair:1] | -| test.swift:420:9:420:34 | call to ... [myPair:1] | test.swift:471:13:471:13 | a [myPair:1] | -| test.swift:420:26:420:33 | call to source() | test.swift:420:9:420:34 | call to ... [myPair:1] | -| test.swift:427:10:427:30 | .myPair(...) [myPair:1] | test.swift:427:29:427:29 | b | -| test.swift:427:29:427:29 | b | test.swift:429:19:429:19 | b | -| test.swift:437:13:437:33 | .myPair(...) [myPair:1] | test.swift:437:32:437:32 | y | -| test.swift:437:32:437:32 | y | test.swift:439:19:439:19 | y | -| test.swift:442:21:442:34 | call to ... [myCons:1, myPair:1] | test.swift:452:14:452:38 | .myCons(...) [myCons:1, myPair:1] | -| test.swift:442:21:442:34 | call to ... [myCons:1, myPair:1] | test.swift:467:17:467:41 | .myCons(...) [myCons:1, myPair:1] | -| test.swift:442:21:442:34 | call to ... [myCons:1, myPair:1] | test.swift:471:16:471:16 | b [myCons:1, myPair:1] | -| test.swift:442:33:442:33 | a [myPair:1] | test.swift:442:21:442:34 | call to ... [myCons:1, myPair:1] | -| test.swift:452:14:452:38 | .myCons(...) [myCons:1, myPair:1] | test.swift:452:25:452:37 | .myPair(...) [myPair:1] | -| test.swift:452:25:452:37 | .myPair(...) [myPair:1] | test.swift:452:36:452:36 | c | -| test.swift:452:36:452:36 | c | test.swift:455:19:455:19 | c | -| test.swift:463:13:463:39 | .myPair(...) [myPair:0] | test.swift:463:31:463:31 | x | -| test.swift:463:31:463:31 | x | test.swift:464:19:464:19 | x | -| test.swift:463:43:463:62 | call to ... [myPair:0] | test.swift:463:13:463:39 | .myPair(...) [myPair:0] | -| test.swift:463:51:463:58 | call to source() | test.swift:463:43:463:62 | call to ... [myPair:0] | -| test.swift:467:17:467:41 | .myCons(...) [myCons:1, myPair:1] | test.swift:467:28:467:40 | .myPair(...) [myPair:1] | -| test.swift:467:28:467:40 | .myPair(...) [myPair:1] | test.swift:467:39:467:39 | c | -| test.swift:467:39:467:39 | c | test.swift:468:19:468:19 | c | -| test.swift:471:12:471:17 | (...) [Tuple element at index 0, myPair:1] | test.swift:472:14:472:55 | (...) [Tuple element at index 0, myPair:1] | -| test.swift:471:12:471:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | test.swift:472:14:472:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | -| test.swift:471:13:471:13 | a [myPair:1] | test.swift:471:12:471:17 | (...) [Tuple element at index 0, myPair:1] | -| test.swift:471:16:471:16 | b [myCons:1, myPair:1] | test.swift:471:12:471:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | -| test.swift:472:14:472:55 | (...) [Tuple element at index 0, myPair:1] | test.swift:472:15:472:27 | .myPair(...) [myPair:1] | -| test.swift:472:14:472:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | test.swift:472:30:472:54 | .myCons(...) [myCons:1, myPair:1] | -| test.swift:472:15:472:27 | .myPair(...) [myPair:1] | test.swift:472:26:472:26 | b | -| test.swift:472:26:472:26 | b | test.swift:474:19:474:19 | b | -| test.swift:472:30:472:54 | .myCons(...) [myCons:1, myPair:1] | test.swift:472:41:472:53 | .myPair(...) [myPair:1] | -| test.swift:472:41:472:53 | .myPair(...) [myPair:1] | test.swift:472:52:472:52 | e | -| test.swift:472:52:472:52 | e | test.swift:477:19:477:19 | e | -| test.swift:486:13:486:28 | call to optionalSource() [some:0] | test.swift:488:8:488:12 | let ...? [some:0] | -| test.swift:486:13:486:28 | call to optionalSource() [some:0] | test.swift:493:19:493:19 | x [some:0] | -| test.swift:488:8:488:12 | let ...? [some:0] | test.swift:488:12:488:12 | a | -| test.swift:488:12:488:12 | a | test.swift:489:19:489:19 | a | -| test.swift:493:18:493:23 | (...) [Tuple element at index 0, some:0] | test.swift:495:10:495:37 | (...) [Tuple element at index 0, some:0] | -| test.swift:493:19:493:19 | x [some:0] | test.swift:493:18:493:23 | (...) [Tuple element at index 0, some:0] | -| test.swift:495:10:495:37 | (...) [Tuple element at index 0, some:0] | test.swift:495:11:495:22 | .some(...) [some:0] | -| test.swift:495:11:495:22 | .some(...) [some:0] | test.swift:495:21:495:21 | a | -| test.swift:495:21:495:21 | a | test.swift:496:19:496:19 | a | -| test.swift:509:9:509:9 | self [x, some:0] | file://:0:0:0:0 | self [x, some:0] | -| test.swift:509:9:509:9 | value [some:0] | file://:0:0:0:0 | value [some:0] | -| test.swift:513:13:513:28 | call to optionalSource() [some:0] | test.swift:515:12:515:12 | x [some:0] | -| test.swift:515:5:515:5 | [post] cx [x, some:0] | test.swift:519:20:519:20 | cx [x, some:0] | -| test.swift:515:12:515:12 | x [some:0] | test.swift:509:9:509:9 | value [some:0] | -| test.swift:515:12:515:12 | x [some:0] | test.swift:515:5:515:5 | [post] cx [x, some:0] | -| test.swift:519:11:519:15 | let ...? [some:0] | test.swift:519:15:519:15 | z1 | -| test.swift:519:15:519:15 | z1 | test.swift:520:15:520:15 | z1 | -| test.swift:519:20:519:20 | cx [x, some:0] | test.swift:509:9:509:9 | self [x, some:0] | -| test.swift:519:20:519:20 | cx [x, some:0] | test.swift:519:20:519:23 | .x [some:0] | -| test.swift:519:20:519:23 | .x [some:0] | test.swift:519:11:519:15 | let ...? [some:0] | -| test.swift:526:14:526:21 | call to source() | test.swift:526:13:526:21 | call to +(_:) | -| test.swift:535:9:535:9 | self [str] | file://:0:0:0:0 | self [str] | -| test.swift:536:10:536:13 | s | test.swift:537:13:537:13 | s | -| test.swift:537:7:537:7 | [post] self [str] | test.swift:536:5:538:5 | self[return] [str] | -| test.swift:537:13:537:13 | s | test.swift:537:7:537:7 | [post] self [str] | -| test.swift:542:17:545:5 | self[return] [str] | test.swift:550:13:550:41 | call to MyClass.init(contentsOfFile:) [str] | -| test.swift:543:7:543:7 | [post] self [str] | test.swift:542:17:545:5 | self[return] [str] | -| test.swift:543:7:543:7 | [post] self [str] | test.swift:544:17:544:17 | self [str] | -| test.swift:543:20:543:28 | call to source3() | test.swift:536:10:536:13 | s | -| test.swift:543:20:543:28 | call to source3() | test.swift:543:7:543:7 | [post] self [str] | -| test.swift:544:17:544:17 | self [str] | test.swift:544:17:544:17 | .str | -| test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | test.swift:535:9:535:9 | self [str] | -| test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | test.swift:549:13:549:35 | .str | -| test.swift:549:24:549:32 | call to source3() | test.swift:536:10:536:13 | s | -| test.swift:549:24:549:32 | call to source3() | test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | -| test.swift:550:13:550:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:535:9:535:9 | self [str] | -| test.swift:550:13:550:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:550:13:550:43 | .str | -| test.swift:567:8:567:11 | x | test.swift:568:14:568:14 | x | -| test.swift:568:5:568:5 | [post] self [x] | test.swift:567:3:569:3 | self[return] [x] | -| test.swift:568:14:568:14 | x | test.swift:568:5:568:5 | [post] self [x] | -| test.swift:573:11:573:24 | call to S.init(x:) [x] | test.swift:575:13:575:13 | s [x] | -| test.swift:573:11:573:24 | call to S.init(x:) [x] | test.swift:578:13:578:13 | s [x] | -| test.swift:573:16:573:23 | call to source() | test.swift:567:8:567:11 | x | -| test.swift:573:16:573:23 | call to source() | test.swift:573:11:573:24 | call to S.init(x:) [x] | -| test.swift:574:11:574:14 | enter #keyPath(...) [x] | test.swift:574:14:574:14 | KeyPathComponent [x] | -| test.swift:574:14:574:14 | KeyPathComponent [x] | test.swift:574:11:574:14 | exit #keyPath(...) | -| test.swift:575:13:575:13 | s [x] | test.swift:574:11:574:14 | enter #keyPath(...) [x] | -| test.swift:575:13:575:13 | s [x] | test.swift:575:13:575:25 | \\...[...] | -| test.swift:577:36:577:38 | enter #keyPath(...) [x] | test.swift:577:38:577:38 | KeyPathComponent [x] | -| test.swift:577:38:577:38 | KeyPathComponent [x] | test.swift:577:36:577:38 | exit #keyPath(...) | -| test.swift:578:13:578:13 | s [x] | test.swift:577:36:577:38 | enter #keyPath(...) [x] | -| test.swift:578:13:578:13 | s [x] | test.swift:578:13:578:32 | \\...[...] | -| test.swift:584:8:584:11 | s [x] | test.swift:585:14:585:14 | s [x] | -| test.swift:585:5:585:5 | [post] self [s, x] | test.swift:584:3:586:3 | self[return] [s, x] | -| test.swift:585:14:585:14 | s [x] | test.swift:585:5:585:5 | [post] self [s, x] | -| test.swift:590:11:590:24 | call to S.init(x:) [x] | test.swift:591:18:591:18 | s [x] | -| test.swift:590:16:590:23 | call to source() | test.swift:567:8:567:11 | x | -| test.swift:590:16:590:23 | call to source() | test.swift:590:11:590:24 | call to S.init(x:) [x] | -| test.swift:591:12:591:19 | call to S2.init(s:) [s, x] | test.swift:593:13:593:13 | s2 [s, x] | -| test.swift:591:18:591:18 | s [x] | test.swift:584:8:584:11 | s [x] | -| test.swift:591:18:591:18 | s [x] | test.swift:591:12:591:19 | call to S2.init(s:) [s, x] | -| test.swift:592:11:592:17 | enter #keyPath(...) [s, x] | test.swift:592:15:592:15 | KeyPathComponent [s, x] | -| test.swift:592:15:592:15 | KeyPathComponent [s, x] | test.swift:592:17:592:17 | KeyPathComponent [x] | -| test.swift:592:17:592:17 | KeyPathComponent [x] | test.swift:592:11:592:17 | exit #keyPath(...) | -| test.swift:593:13:593:13 | s2 [s, x] | test.swift:592:11:592:17 | enter #keyPath(...) [s, x] | -| test.swift:593:13:593:13 | s2 [s, x] | test.swift:593:13:593:26 | \\...[...] | -| test.swift:618:13:618:20 | call to source() | test.swift:626:15:626:15 | y | -| test.swift:628:9:628:16 | call to source() | test.swift:630:10:630:11 | &... | -| test.swift:628:9:628:16 | call to source() | test.swift:631:15:631:15 | x | -| test.swift:630:10:630:11 | &... | test.swift:630:14:630:15 | [post] &... | -| test.swift:630:14:630:15 | [post] &... | test.swift:632:15:632:15 | y | +| test.swift:375:16:375:21 | v | test.swift:375:61:375:61 | v | +| test.swift:375:61:375:61 | v | test.swift:375:45:375:62 | call to ... [mySingle:0] | +| test.swift:377:18:377:23 | v | test.swift:377:59:377:59 | v | +| test.swift:377:59:377:59 | v | test.swift:377:45:377:60 | call to ... [some:0] | +| test.swift:403:9:403:27 | call to ... [mySingle:0] | test.swift:408:10:408:25 | .mySingle(...) [mySingle:0] | +| test.swift:403:9:403:27 | call to ... [mySingle:0] | test.swift:417:13:417:28 | .mySingle(...) [mySingle:0] | +| test.swift:403:19:403:26 | call to source() | test.swift:403:9:403:27 | call to ... [mySingle:0] | +| test.swift:408:10:408:25 | .mySingle(...) [mySingle:0] | test.swift:408:24:408:24 | a | +| test.swift:408:24:408:24 | a | test.swift:409:19:409:19 | a | +| test.swift:417:13:417:28 | .mySingle(...) [mySingle:0] | test.swift:417:27:417:27 | x | +| test.swift:417:27:417:27 | x | test.swift:418:19:418:19 | x | +| test.swift:425:9:425:34 | call to ... [myPair:1] | test.swift:432:10:432:30 | .myPair(...) [myPair:1] | +| test.swift:425:9:425:34 | call to ... [myPair:1] | test.swift:442:13:442:33 | .myPair(...) [myPair:1] | +| test.swift:425:9:425:34 | call to ... [myPair:1] | test.swift:447:33:447:33 | a [myPair:1] | +| test.swift:425:9:425:34 | call to ... [myPair:1] | test.swift:476:13:476:13 | a [myPair:1] | +| test.swift:425:26:425:33 | call to source() | test.swift:425:9:425:34 | call to ... [myPair:1] | +| test.swift:432:10:432:30 | .myPair(...) [myPair:1] | test.swift:432:29:432:29 | b | +| test.swift:432:29:432:29 | b | test.swift:434:19:434:19 | b | +| test.swift:442:13:442:33 | .myPair(...) [myPair:1] | test.swift:442:32:442:32 | y | +| test.swift:442:32:442:32 | y | test.swift:444:19:444:19 | y | +| test.swift:447:21:447:34 | call to ... [myCons:1, myPair:1] | test.swift:457:14:457:38 | .myCons(...) [myCons:1, myPair:1] | +| test.swift:447:21:447:34 | call to ... [myCons:1, myPair:1] | test.swift:472:17:472:41 | .myCons(...) [myCons:1, myPair:1] | +| test.swift:447:21:447:34 | call to ... [myCons:1, myPair:1] | test.swift:476:16:476:16 | b [myCons:1, myPair:1] | +| test.swift:447:33:447:33 | a [myPair:1] | test.swift:447:21:447:34 | call to ... [myCons:1, myPair:1] | +| test.swift:457:14:457:38 | .myCons(...) [myCons:1, myPair:1] | test.swift:457:25:457:37 | .myPair(...) [myPair:1] | +| test.swift:457:25:457:37 | .myPair(...) [myPair:1] | test.swift:457:36:457:36 | c | +| test.swift:457:36:457:36 | c | test.swift:460:19:460:19 | c | +| test.swift:468:13:468:39 | .myPair(...) [myPair:0] | test.swift:468:31:468:31 | x | +| test.swift:468:31:468:31 | x | test.swift:469:19:469:19 | x | +| test.swift:468:43:468:62 | call to ... [myPair:0] | test.swift:468:13:468:39 | .myPair(...) [myPair:0] | +| test.swift:468:51:468:58 | call to source() | test.swift:468:43:468:62 | call to ... [myPair:0] | +| test.swift:472:17:472:41 | .myCons(...) [myCons:1, myPair:1] | test.swift:472:28:472:40 | .myPair(...) [myPair:1] | +| test.swift:472:28:472:40 | .myPair(...) [myPair:1] | test.swift:472:39:472:39 | c | +| test.swift:472:39:472:39 | c | test.swift:473:19:473:19 | c | +| test.swift:476:12:476:17 | (...) [Tuple element at index 0, myPair:1] | test.swift:477:14:477:55 | (...) [Tuple element at index 0, myPair:1] | +| test.swift:476:12:476:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | test.swift:477:14:477:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | +| test.swift:476:13:476:13 | a [myPair:1] | test.swift:476:12:476:17 | (...) [Tuple element at index 0, myPair:1] | +| test.swift:476:16:476:16 | b [myCons:1, myPair:1] | test.swift:476:12:476:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | +| test.swift:477:14:477:55 | (...) [Tuple element at index 0, myPair:1] | test.swift:477:15:477:27 | .myPair(...) [myPair:1] | +| test.swift:477:14:477:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | test.swift:477:30:477:54 | .myCons(...) [myCons:1, myPair:1] | +| test.swift:477:15:477:27 | .myPair(...) [myPair:1] | test.swift:477:26:477:26 | b | +| test.swift:477:26:477:26 | b | test.swift:479:19:479:19 | b | +| test.swift:477:30:477:54 | .myCons(...) [myCons:1, myPair:1] | test.swift:477:41:477:53 | .myPair(...) [myPair:1] | +| test.swift:477:41:477:53 | .myPair(...) [myPair:1] | test.swift:477:52:477:52 | e | +| test.swift:477:52:477:52 | e | test.swift:482:19:482:19 | e | +| test.swift:488:14:488:38 | call to ... [mySingle:0] | test.swift:494:13:494:35 | .mySingle(...) [mySingle:0] | +| test.swift:488:30:488:37 | call to source() | test.swift:488:14:488:38 | call to ... [mySingle:0] | +| test.swift:490:14:490:32 | call to mkMyEnum1(_:) [mySingle:0] | test.swift:496:13:496:35 | .mySingle(...) [mySingle:0] | +| test.swift:490:24:490:31 | call to source() | test.swift:375:16:375:21 | v | +| test.swift:490:24:490:31 | call to source() | test.swift:490:14:490:32 | call to mkMyEnum1(_:) [mySingle:0] | +| test.swift:492:14:492:32 | call to mkMyEnum2(_:) [mySingle:0] | test.swift:498:13:498:35 | .mySingle(...) [mySingle:0] | +| test.swift:492:24:492:31 | call to source() | test.swift:492:14:492:32 | call to mkMyEnum2(_:) [mySingle:0] | +| test.swift:494:13:494:35 | .mySingle(...) [mySingle:0] | test.swift:494:33:494:33 | d2 | +| test.swift:494:33:494:33 | d2 | test.swift:494:54:494:54 | d2 | +| test.swift:496:13:496:35 | .mySingle(...) [mySingle:0] | test.swift:496:33:496:33 | d4 | +| test.swift:496:33:496:33 | d4 | test.swift:496:54:496:54 | d4 | +| test.swift:498:13:498:35 | .mySingle(...) [mySingle:0] | test.swift:498:33:498:33 | d6 | +| test.swift:498:33:498:33 | d6 | test.swift:498:54:498:54 | d6 | +| test.swift:501:14:501:36 | call to ... [some:0] | test.swift:507:15:507:15 | e2 [some:0] | +| test.swift:501:28:501:35 | call to source() | test.swift:501:14:501:36 | call to ... [some:0] | +| test.swift:503:14:503:34 | call to mkOptional1(_:) [some:0] | test.swift:509:15:509:15 | e4 [some:0] | +| test.swift:503:26:503:33 | call to source() | test.swift:377:18:377:23 | v | +| test.swift:503:26:503:33 | call to source() | test.swift:503:14:503:34 | call to mkOptional1(_:) [some:0] | +| test.swift:505:14:505:34 | call to mkOptional2(_:) [some:0] | test.swift:511:15:511:15 | e6 [some:0] | +| test.swift:505:26:505:33 | call to source() | test.swift:505:14:505:34 | call to mkOptional2(_:) [some:0] | +| test.swift:507:15:507:15 | e2 [some:0] | test.swift:507:15:507:17 | ...! | +| test.swift:509:15:509:15 | e4 [some:0] | test.swift:509:15:509:17 | ...! | +| test.swift:511:15:511:15 | e6 [some:0] | test.swift:511:15:511:17 | ...! | +| test.swift:517:13:517:28 | call to optionalSource() [some:0] | test.swift:519:8:519:12 | let ...? [some:0] | +| test.swift:517:13:517:28 | call to optionalSource() [some:0] | test.swift:524:19:524:19 | x [some:0] | +| test.swift:519:8:519:12 | let ...? [some:0] | test.swift:519:12:519:12 | a | +| test.swift:519:12:519:12 | a | test.swift:520:19:520:19 | a | +| test.swift:524:18:524:23 | (...) [Tuple element at index 0, some:0] | test.swift:526:10:526:37 | (...) [Tuple element at index 0, some:0] | +| test.swift:524:19:524:19 | x [some:0] | test.swift:524:18:524:23 | (...) [Tuple element at index 0, some:0] | +| test.swift:526:10:526:37 | (...) [Tuple element at index 0, some:0] | test.swift:526:11:526:22 | .some(...) [some:0] | +| test.swift:526:11:526:22 | .some(...) [some:0] | test.swift:526:21:526:21 | a | +| test.swift:526:21:526:21 | a | test.swift:527:19:527:19 | a | +| test.swift:540:9:540:9 | self [x, some:0] | file://:0:0:0:0 | self [x, some:0] | +| test.swift:540:9:540:9 | value [some:0] | file://:0:0:0:0 | value [some:0] | +| test.swift:544:13:544:28 | call to optionalSource() [some:0] | test.swift:546:12:546:12 | x [some:0] | +| test.swift:546:5:546:5 | [post] cx [x, some:0] | test.swift:550:20:550:20 | cx [x, some:0] | +| test.swift:546:12:546:12 | x [some:0] | test.swift:540:9:540:9 | value [some:0] | +| test.swift:546:12:546:12 | x [some:0] | test.swift:546:5:546:5 | [post] cx [x, some:0] | +| test.swift:550:11:550:15 | let ...? [some:0] | test.swift:550:15:550:15 | z1 | +| test.swift:550:15:550:15 | z1 | test.swift:551:15:551:15 | z1 | +| test.swift:550:20:550:20 | cx [x, some:0] | test.swift:540:9:540:9 | self [x, some:0] | +| test.swift:550:20:550:20 | cx [x, some:0] | test.swift:550:20:550:23 | .x [some:0] | +| test.swift:550:20:550:23 | .x [some:0] | test.swift:550:11:550:15 | let ...? [some:0] | +| test.swift:557:14:557:21 | call to source() | test.swift:557:13:557:21 | call to +(_:) | +| test.swift:566:9:566:9 | self [str] | file://:0:0:0:0 | self [str] | +| test.swift:567:10:567:13 | s | test.swift:568:13:568:13 | s | +| test.swift:568:7:568:7 | [post] self [str] | test.swift:567:5:569:5 | self[return] [str] | +| test.swift:568:13:568:13 | s | test.swift:568:7:568:7 | [post] self [str] | +| test.swift:573:17:576:5 | self[return] [str] | test.swift:581:13:581:41 | call to MyClass.init(contentsOfFile:) [str] | +| test.swift:574:7:574:7 | [post] self [str] | test.swift:573:17:576:5 | self[return] [str] | +| test.swift:574:7:574:7 | [post] self [str] | test.swift:575:17:575:17 | self [str] | +| test.swift:574:20:574:28 | call to source3() | test.swift:567:10:567:13 | s | +| test.swift:574:20:574:28 | call to source3() | test.swift:574:7:574:7 | [post] self [str] | +| test.swift:575:17:575:17 | self [str] | test.swift:575:17:575:17 | .str | +| test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | test.swift:566:9:566:9 | self [str] | +| test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | test.swift:580:13:580:35 | .str | +| test.swift:580:24:580:32 | call to source3() | test.swift:567:10:567:13 | s | +| test.swift:580:24:580:32 | call to source3() | test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | +| test.swift:581:13:581:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:566:9:566:9 | self [str] | +| test.swift:581:13:581:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:581:13:581:43 | .str | +| test.swift:598:8:598:11 | x | test.swift:599:14:599:14 | x | +| test.swift:599:5:599:5 | [post] self [x] | test.swift:598:3:600:3 | self[return] [x] | +| test.swift:599:14:599:14 | x | test.swift:599:5:599:5 | [post] self [x] | +| test.swift:604:11:604:24 | call to S.init(x:) [x] | test.swift:606:13:606:13 | s [x] | +| test.swift:604:11:604:24 | call to S.init(x:) [x] | test.swift:609:13:609:13 | s [x] | +| test.swift:604:16:604:23 | call to source() | test.swift:598:8:598:11 | x | +| test.swift:604:16:604:23 | call to source() | test.swift:604:11:604:24 | call to S.init(x:) [x] | +| test.swift:605:11:605:14 | enter #keyPath(...) [x] | test.swift:605:14:605:14 | KeyPathComponent [x] | +| test.swift:605:14:605:14 | KeyPathComponent [x] | test.swift:605:11:605:14 | exit #keyPath(...) | +| test.swift:606:13:606:13 | s [x] | test.swift:605:11:605:14 | enter #keyPath(...) [x] | +| test.swift:606:13:606:13 | s [x] | test.swift:606:13:606:25 | \\...[...] | +| test.swift:608:36:608:38 | enter #keyPath(...) [x] | test.swift:608:38:608:38 | KeyPathComponent [x] | +| test.swift:608:38:608:38 | KeyPathComponent [x] | test.swift:608:36:608:38 | exit #keyPath(...) | +| test.swift:609:13:609:13 | s [x] | test.swift:608:36:608:38 | enter #keyPath(...) [x] | +| test.swift:609:13:609:13 | s [x] | test.swift:609:13:609:32 | \\...[...] | +| test.swift:615:8:615:11 | s [x] | test.swift:616:14:616:14 | s [x] | +| test.swift:616:5:616:5 | [post] self [s, x] | test.swift:615:3:617:3 | self[return] [s, x] | +| test.swift:616:14:616:14 | s [x] | test.swift:616:5:616:5 | [post] self [s, x] | +| test.swift:621:11:621:24 | call to S.init(x:) [x] | test.swift:622:18:622:18 | s [x] | +| test.swift:621:16:621:23 | call to source() | test.swift:598:8:598:11 | x | +| test.swift:621:16:621:23 | call to source() | test.swift:621:11:621:24 | call to S.init(x:) [x] | +| test.swift:622:12:622:19 | call to S2.init(s:) [s, x] | test.swift:624:13:624:13 | s2 [s, x] | +| test.swift:622:18:622:18 | s [x] | test.swift:615:8:615:11 | s [x] | +| test.swift:622:18:622:18 | s [x] | test.swift:622:12:622:19 | call to S2.init(s:) [s, x] | +| test.swift:623:11:623:17 | enter #keyPath(...) [s, x] | test.swift:623:15:623:15 | KeyPathComponent [s, x] | +| test.swift:623:15:623:15 | KeyPathComponent [s, x] | test.swift:623:17:623:17 | KeyPathComponent [x] | +| test.swift:623:17:623:17 | KeyPathComponent [x] | test.swift:623:11:623:17 | exit #keyPath(...) | +| test.swift:624:13:624:13 | s2 [s, x] | test.swift:623:11:623:17 | enter #keyPath(...) [s, x] | +| test.swift:624:13:624:13 | s2 [s, x] | test.swift:624:13:624:26 | \\...[...] | +| test.swift:628:17:628:26 | [...] [Array element] | test.swift:630:15:630:15 | array [Array element] | +| test.swift:628:18:628:25 | call to source() | test.swift:628:17:628:26 | [...] [Array element] | +| test.swift:629:13:629:22 | enter #keyPath(...) [Array element] | test.swift:629:20:629:22 | KeyPathComponent [Array element] | +| test.swift:629:20:629:22 | KeyPathComponent [Array element] | test.swift:629:13:629:22 | exit #keyPath(...) | +| test.swift:630:15:630:15 | array [Array element] | test.swift:629:13:629:22 | enter #keyPath(...) [Array element] | +| test.swift:630:15:630:15 | array [Array element] | test.swift:630:15:630:31 | \\...[...] | +| test.swift:649:13:649:20 | call to source() | test.swift:657:15:657:15 | y | +| test.swift:659:9:659:16 | call to source() | test.swift:661:11:661:11 | x | +| test.swift:659:9:659:16 | call to source() | test.swift:662:15:662:15 | x | +| test.swift:661:11:661:11 | x | test.swift:661:15:661:15 | [post] y | +| test.swift:661:15:661:15 | [post] y | test.swift:663:15:663:15 | y | +| test.swift:669:5:669:5 | [post] arr1 [Array element] | test.swift:670:15:670:15 | arr1 [Array element] | +| test.swift:669:15:669:22 | call to source() | test.swift:669:5:669:5 | [post] arr1 [Array element] | +| test.swift:670:15:670:15 | arr1 [Array element] | test.swift:670:15:670:21 | ...[...] | +| test.swift:673:16:673:25 | [...] [Array element] | test.swift:674:15:674:15 | arr2 [Array element] | +| test.swift:673:17:673:24 | call to source() | test.swift:673:16:673:25 | [...] [Array element] | +| test.swift:674:15:674:15 | arr2 [Array element] | test.swift:674:15:674:21 | ...[...] | +| test.swift:676:18:676:29 | [...] [Array element, Array element] | test.swift:678:15:678:15 | matrix [Array element, Array element] | +| test.swift:676:19:676:28 | [...] [Array element] | test.swift:676:18:676:29 | [...] [Array element, Array element] | +| test.swift:676:20:676:27 | call to source() | test.swift:676:19:676:28 | [...] [Array element] | +| test.swift:678:15:678:15 | matrix [Array element, Array element] | test.swift:678:15:678:23 | ...[...] [Array element] | +| test.swift:678:15:678:23 | ...[...] [Array element] | test.swift:678:15:678:26 | ...[...] | +| test.swift:681:5:681:5 | [post] matrix2 [Array element, Array element] | test.swift:682:15:682:15 | matrix2 [Array element, Array element] | +| test.swift:681:5:681:14 | [post] getter for ...[...] [Array element] | test.swift:681:5:681:5 | [post] matrix2 [Array element, Array element] | +| test.swift:681:21:681:28 | call to source() | test.swift:681:5:681:14 | [post] getter for ...[...] [Array element] | +| test.swift:682:15:682:15 | matrix2 [Array element, Array element] | test.swift:682:15:682:24 | ...[...] [Array element] | +| test.swift:682:15:682:24 | ...[...] [Array element] | test.swift:682:15:682:27 | ...[...] | +| test.swift:693:5:693:5 | [post] arr6 [Array element] | test.swift:694:15:694:15 | arr6 [Array element] | +| test.swift:693:17:693:24 | call to source() | test.swift:693:5:693:5 | [post] arr6 [Array element] | +| test.swift:694:15:694:15 | arr6 [Array element] | test.swift:694:15:694:21 | ...[...] | nodes | file://:0:0:0:0 | .a [x] | semmle.label | .a [x] | | file://:0:0:0:0 | .str | semmle.label | .str | @@ -303,13 +365,13 @@ nodes | test.swift:50:15:50:15 | t | semmle.label | t | | test.swift:53:1:56:1 | arg[return] | semmle.label | arg[return] | | test.swift:54:11:54:18 | call to source() | semmle.label | call to source() | -| test.swift:61:22:61:23 | [post] &... | semmle.label | [post] &... | +| test.swift:61:23:61:23 | [post] x | semmle.label | [post] x | | test.swift:62:15:62:15 | x | semmle.label | x | | test.swift:65:1:70:1 | arg2[return] | semmle.label | arg2[return] | | test.swift:65:16:65:28 | arg1 | semmle.label | arg1 | | test.swift:73:18:73:25 | call to source() | semmle.label | call to source() | -| test.swift:75:21:75:22 | &... | semmle.label | &... | -| test.swift:75:31:75:32 | [post] &... | semmle.label | [post] &... | +| test.swift:75:22:75:22 | x | semmle.label | x | +| test.swift:75:32:75:32 | [post] y | semmle.label | [post] y | | test.swift:76:15:76:15 | x | semmle.label | x | | test.swift:77:15:77:15 | y | semmle.label | y | | test.swift:80:1:82:1 | arg[return] | semmle.label | arg[return] | @@ -317,9 +379,9 @@ nodes | test.swift:84:1:91:1 | arg[return] | semmle.label | arg[return] | | test.swift:86:15:86:22 | call to source() | semmle.label | call to source() | | test.swift:89:15:89:22 | call to source() | semmle.label | call to source() | -| test.swift:97:39:97:40 | [post] &... | semmle.label | [post] &... | +| test.swift:97:40:97:40 | [post] x | semmle.label | [post] x | | test.swift:98:19:98:19 | x | semmle.label | x | -| test.swift:104:40:104:41 | [post] &... | semmle.label | [post] &... | +| test.swift:104:41:104:41 | [post] x | semmle.label | [post] x | | test.swift:105:19:105:19 | x | semmle.label | x | | test.swift:109:9:109:14 | arg | semmle.label | arg | | test.swift:110:12:110:12 | arg | semmle.label | arg | @@ -395,6 +457,7 @@ nodes | test.swift:263:13:263:28 | call to optionalSource() | semmle.label | call to optionalSource() | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | semmle.label | call to optionalSource() [some:0] | | test.swift:265:15:265:15 | x | semmle.label | x | +| test.swift:267:15:267:15 | x [some:0] | semmle.label | x [some:0] | | test.swift:267:15:267:16 | ...! | semmle.label | ...! | | test.swift:270:15:270:22 | call to source() | semmle.label | call to source() | | test.swift:270:15:270:31 | call to signum() | semmle.label | call to signum() | @@ -404,7 +467,11 @@ nodes | test.swift:274:15:274:20 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | | test.swift:275:15:275:27 | ... ??(_:_:) ... | semmle.label | ... ??(_:_:) ... | | test.swift:279:15:279:31 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.swift:279:26:279:26 | x [some:0] | semmle.label | x [some:0] | +| test.swift:279:26:279:27 | ...! | semmle.label | ...! | | test.swift:280:15:280:38 | ... ? ... : ... | semmle.label | ... ? ... : ... | +| test.swift:280:26:280:26 | x [some:0] | semmle.label | x [some:0] | +| test.swift:280:26:280:27 | ...! | semmle.label | ...! | | test.swift:280:31:280:38 | call to source() | semmle.label | call to source() | | test.swift:282:15:282:38 | ... ? ... : ... | semmle.label | ... ? ... : ... | | test.swift:282:31:282:38 | call to source() | semmle.label | call to source() | @@ -421,6 +488,7 @@ nodes | test.swift:298:11:298:15 | let ...? [some:0] | semmle.label | let ...? [some:0] | | test.swift:298:15:298:15 | z1 | semmle.label | z1 | | test.swift:300:15:300:15 | z1 | semmle.label | z1 | +| test.swift:303:15:303:15 | x [some:0] | semmle.label | x [some:0] | | test.swift:303:15:303:16 | ...! | semmle.label | ...! | | test.swift:303:15:303:25 | call to signum() | semmle.label | call to signum() | | test.swift:306:13:306:24 | .some(...) [some:0] | semmle.label | .some(...) [some:0] | @@ -455,127 +523,191 @@ nodes | test.swift:361:15:361:18 | .1 | semmle.label | .1 | | test.swift:363:15:363:15 | a | semmle.label | a | | test.swift:364:15:364:15 | b | semmle.label | b | -| test.swift:398:9:398:27 | call to ... [mySingle:0] | semmle.label | call to ... [mySingle:0] | -| test.swift:398:19:398:26 | call to source() | semmle.label | call to source() | -| test.swift:403:10:403:25 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | -| test.swift:403:24:403:24 | a | semmle.label | a | -| test.swift:404:19:404:19 | a | semmle.label | a | -| test.swift:412:13:412:28 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | -| test.swift:412:27:412:27 | x | semmle.label | x | -| test.swift:413:19:413:19 | x | semmle.label | x | -| test.swift:420:9:420:34 | call to ... [myPair:1] | semmle.label | call to ... [myPair:1] | -| test.swift:420:26:420:33 | call to source() | semmle.label | call to source() | -| test.swift:427:10:427:30 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:427:29:427:29 | b | semmle.label | b | -| test.swift:429:19:429:19 | b | semmle.label | b | -| test.swift:437:13:437:33 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:437:32:437:32 | y | semmle.label | y | -| test.swift:439:19:439:19 | y | semmle.label | y | -| test.swift:442:21:442:34 | call to ... [myCons:1, myPair:1] | semmle.label | call to ... [myCons:1, myPair:1] | -| test.swift:442:33:442:33 | a [myPair:1] | semmle.label | a [myPair:1] | -| test.swift:452:14:452:38 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | -| test.swift:452:25:452:37 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:452:36:452:36 | c | semmle.label | c | -| test.swift:455:19:455:19 | c | semmle.label | c | -| test.swift:463:13:463:39 | .myPair(...) [myPair:0] | semmle.label | .myPair(...) [myPair:0] | -| test.swift:463:31:463:31 | x | semmle.label | x | -| test.swift:463:43:463:62 | call to ... [myPair:0] | semmle.label | call to ... [myPair:0] | -| test.swift:463:51:463:58 | call to source() | semmle.label | call to source() | -| test.swift:464:19:464:19 | x | semmle.label | x | -| test.swift:467:17:467:41 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | -| test.swift:467:28:467:40 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:467:39:467:39 | c | semmle.label | c | -| test.swift:468:19:468:19 | c | semmle.label | c | -| test.swift:471:12:471:17 | (...) [Tuple element at index 0, myPair:1] | semmle.label | (...) [Tuple element at index 0, myPair:1] | -| test.swift:471:12:471:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | semmle.label | (...) [Tuple element at index 1, myCons:1, myPair:1] | -| test.swift:471:13:471:13 | a [myPair:1] | semmle.label | a [myPair:1] | -| test.swift:471:16:471:16 | b [myCons:1, myPair:1] | semmle.label | b [myCons:1, myPair:1] | -| test.swift:472:14:472:55 | (...) [Tuple element at index 0, myPair:1] | semmle.label | (...) [Tuple element at index 0, myPair:1] | -| test.swift:472:14:472:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | semmle.label | (...) [Tuple element at index 1, myCons:1, myPair:1] | -| test.swift:472:15:472:27 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:472:26:472:26 | b | semmle.label | b | -| test.swift:472:30:472:54 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | -| test.swift:472:41:472:53 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | -| test.swift:472:52:472:52 | e | semmle.label | e | -| test.swift:474:19:474:19 | b | semmle.label | b | -| test.swift:477:19:477:19 | e | semmle.label | e | -| test.swift:486:13:486:28 | call to optionalSource() [some:0] | semmle.label | call to optionalSource() [some:0] | -| test.swift:488:8:488:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| test.swift:488:12:488:12 | a | semmle.label | a | -| test.swift:489:19:489:19 | a | semmle.label | a | -| test.swift:493:18:493:23 | (...) [Tuple element at index 0, some:0] | semmle.label | (...) [Tuple element at index 0, some:0] | -| test.swift:493:19:493:19 | x [some:0] | semmle.label | x [some:0] | -| test.swift:495:10:495:37 | (...) [Tuple element at index 0, some:0] | semmle.label | (...) [Tuple element at index 0, some:0] | -| test.swift:495:11:495:22 | .some(...) [some:0] | semmle.label | .some(...) [some:0] | -| test.swift:495:21:495:21 | a | semmle.label | a | -| test.swift:496:19:496:19 | a | semmle.label | a | -| test.swift:509:9:509:9 | self [x, some:0] | semmle.label | self [x, some:0] | -| test.swift:509:9:509:9 | value [some:0] | semmle.label | value [some:0] | -| test.swift:513:13:513:28 | call to optionalSource() [some:0] | semmle.label | call to optionalSource() [some:0] | -| test.swift:515:5:515:5 | [post] cx [x, some:0] | semmle.label | [post] cx [x, some:0] | -| test.swift:515:12:515:12 | x [some:0] | semmle.label | x [some:0] | -| test.swift:519:11:519:15 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| test.swift:519:15:519:15 | z1 | semmle.label | z1 | -| test.swift:519:20:519:20 | cx [x, some:0] | semmle.label | cx [x, some:0] | -| test.swift:519:20:519:23 | .x [some:0] | semmle.label | .x [some:0] | -| test.swift:520:15:520:15 | z1 | semmle.label | z1 | -| test.swift:526:13:526:21 | call to +(_:) | semmle.label | call to +(_:) | -| test.swift:526:14:526:21 | call to source() | semmle.label | call to source() | -| test.swift:527:14:527:21 | call to source() | semmle.label | call to source() | -| test.swift:535:9:535:9 | self [str] | semmle.label | self [str] | -| test.swift:536:5:538:5 | self[return] [str] | semmle.label | self[return] [str] | -| test.swift:536:10:536:13 | s | semmle.label | s | -| test.swift:537:7:537:7 | [post] self [str] | semmle.label | [post] self [str] | -| test.swift:537:13:537:13 | s | semmle.label | s | -| test.swift:542:17:545:5 | self[return] [str] | semmle.label | self[return] [str] | -| test.swift:543:7:543:7 | [post] self [str] | semmle.label | [post] self [str] | -| test.swift:543:20:543:28 | call to source3() | semmle.label | call to source3() | -| test.swift:544:17:544:17 | .str | semmle.label | .str | -| test.swift:544:17:544:17 | self [str] | semmle.label | self [str] | -| test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | semmle.label | call to MyClass.init(s:) [str] | -| test.swift:549:13:549:35 | .str | semmle.label | .str | -| test.swift:549:24:549:32 | call to source3() | semmle.label | call to source3() | -| test.swift:550:13:550:41 | call to MyClass.init(contentsOfFile:) [str] | semmle.label | call to MyClass.init(contentsOfFile:) [str] | -| test.swift:550:13:550:43 | .str | semmle.label | .str | -| test.swift:567:3:569:3 | self[return] [x] | semmle.label | self[return] [x] | -| test.swift:567:8:567:11 | x | semmle.label | x | -| test.swift:568:5:568:5 | [post] self [x] | semmle.label | [post] self [x] | -| test.swift:568:14:568:14 | x | semmle.label | x | -| test.swift:573:11:573:24 | call to S.init(x:) [x] | semmle.label | call to S.init(x:) [x] | -| test.swift:573:16:573:23 | call to source() | semmle.label | call to source() | -| test.swift:574:11:574:14 | enter #keyPath(...) [x] | semmle.label | enter #keyPath(...) [x] | -| test.swift:574:11:574:14 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | -| test.swift:574:14:574:14 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | -| test.swift:575:13:575:13 | s [x] | semmle.label | s [x] | -| test.swift:575:13:575:25 | \\...[...] | semmle.label | \\...[...] | -| test.swift:577:36:577:38 | enter #keyPath(...) [x] | semmle.label | enter #keyPath(...) [x] | -| test.swift:577:36:577:38 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | -| test.swift:577:38:577:38 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | -| test.swift:578:13:578:13 | s [x] | semmle.label | s [x] | -| test.swift:578:13:578:32 | \\...[...] | semmle.label | \\...[...] | -| test.swift:584:3:586:3 | self[return] [s, x] | semmle.label | self[return] [s, x] | -| test.swift:584:8:584:11 | s [x] | semmle.label | s [x] | -| test.swift:585:5:585:5 | [post] self [s, x] | semmle.label | [post] self [s, x] | -| test.swift:585:14:585:14 | s [x] | semmle.label | s [x] | -| test.swift:590:11:590:24 | call to S.init(x:) [x] | semmle.label | call to S.init(x:) [x] | -| test.swift:590:16:590:23 | call to source() | semmle.label | call to source() | -| test.swift:591:12:591:19 | call to S2.init(s:) [s, x] | semmle.label | call to S2.init(s:) [s, x] | -| test.swift:591:18:591:18 | s [x] | semmle.label | s [x] | -| test.swift:592:11:592:17 | enter #keyPath(...) [s, x] | semmle.label | enter #keyPath(...) [s, x] | -| test.swift:592:11:592:17 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | -| test.swift:592:15:592:15 | KeyPathComponent [s, x] | semmle.label | KeyPathComponent [s, x] | -| test.swift:592:17:592:17 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | -| test.swift:593:13:593:13 | s2 [s, x] | semmle.label | s2 [s, x] | -| test.swift:593:13:593:26 | \\...[...] | semmle.label | \\...[...] | -| test.swift:618:13:618:20 | call to source() | semmle.label | call to source() | -| test.swift:626:15:626:15 | y | semmle.label | y | -| test.swift:628:9:628:16 | call to source() | semmle.label | call to source() | -| test.swift:630:10:630:11 | &... | semmle.label | &... | -| test.swift:630:14:630:15 | [post] &... | semmle.label | [post] &... | -| test.swift:631:15:631:15 | x | semmle.label | x | -| test.swift:632:15:632:15 | y | semmle.label | y | +| test.swift:375:16:375:21 | v | semmle.label | v | +| test.swift:375:45:375:62 | call to ... [mySingle:0] | semmle.label | call to ... [mySingle:0] | +| test.swift:375:61:375:61 | v | semmle.label | v | +| test.swift:377:18:377:23 | v | semmle.label | v | +| test.swift:377:45:377:60 | call to ... [some:0] | semmle.label | call to ... [some:0] | +| test.swift:377:59:377:59 | v | semmle.label | v | +| test.swift:403:9:403:27 | call to ... [mySingle:0] | semmle.label | call to ... [mySingle:0] | +| test.swift:403:19:403:26 | call to source() | semmle.label | call to source() | +| test.swift:408:10:408:25 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | +| test.swift:408:24:408:24 | a | semmle.label | a | +| test.swift:409:19:409:19 | a | semmle.label | a | +| test.swift:417:13:417:28 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | +| test.swift:417:27:417:27 | x | semmle.label | x | +| test.swift:418:19:418:19 | x | semmle.label | x | +| test.swift:425:9:425:34 | call to ... [myPair:1] | semmle.label | call to ... [myPair:1] | +| test.swift:425:26:425:33 | call to source() | semmle.label | call to source() | +| test.swift:432:10:432:30 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:432:29:432:29 | b | semmle.label | b | +| test.swift:434:19:434:19 | b | semmle.label | b | +| test.swift:442:13:442:33 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:442:32:442:32 | y | semmle.label | y | +| test.swift:444:19:444:19 | y | semmle.label | y | +| test.swift:447:21:447:34 | call to ... [myCons:1, myPair:1] | semmle.label | call to ... [myCons:1, myPair:1] | +| test.swift:447:33:447:33 | a [myPair:1] | semmle.label | a [myPair:1] | +| test.swift:457:14:457:38 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | +| test.swift:457:25:457:37 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:457:36:457:36 | c | semmle.label | c | +| test.swift:460:19:460:19 | c | semmle.label | c | +| test.swift:468:13:468:39 | .myPair(...) [myPair:0] | semmle.label | .myPair(...) [myPair:0] | +| test.swift:468:31:468:31 | x | semmle.label | x | +| test.swift:468:43:468:62 | call to ... [myPair:0] | semmle.label | call to ... [myPair:0] | +| test.swift:468:51:468:58 | call to source() | semmle.label | call to source() | +| test.swift:469:19:469:19 | x | semmle.label | x | +| test.swift:472:17:472:41 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | +| test.swift:472:28:472:40 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:472:39:472:39 | c | semmle.label | c | +| test.swift:473:19:473:19 | c | semmle.label | c | +| test.swift:476:12:476:17 | (...) [Tuple element at index 0, myPair:1] | semmle.label | (...) [Tuple element at index 0, myPair:1] | +| test.swift:476:12:476:17 | (...) [Tuple element at index 1, myCons:1, myPair:1] | semmle.label | (...) [Tuple element at index 1, myCons:1, myPair:1] | +| test.swift:476:13:476:13 | a [myPair:1] | semmle.label | a [myPair:1] | +| test.swift:476:16:476:16 | b [myCons:1, myPair:1] | semmle.label | b [myCons:1, myPair:1] | +| test.swift:477:14:477:55 | (...) [Tuple element at index 0, myPair:1] | semmle.label | (...) [Tuple element at index 0, myPair:1] | +| test.swift:477:14:477:55 | (...) [Tuple element at index 1, myCons:1, myPair:1] | semmle.label | (...) [Tuple element at index 1, myCons:1, myPair:1] | +| test.swift:477:15:477:27 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:477:26:477:26 | b | semmle.label | b | +| test.swift:477:30:477:54 | .myCons(...) [myCons:1, myPair:1] | semmle.label | .myCons(...) [myCons:1, myPair:1] | +| test.swift:477:41:477:53 | .myPair(...) [myPair:1] | semmle.label | .myPair(...) [myPair:1] | +| test.swift:477:52:477:52 | e | semmle.label | e | +| test.swift:479:19:479:19 | b | semmle.label | b | +| test.swift:482:19:482:19 | e | semmle.label | e | +| test.swift:488:14:488:38 | call to ... [mySingle:0] | semmle.label | call to ... [mySingle:0] | +| test.swift:488:30:488:37 | call to source() | semmle.label | call to source() | +| test.swift:490:14:490:32 | call to mkMyEnum1(_:) [mySingle:0] | semmle.label | call to mkMyEnum1(_:) [mySingle:0] | +| test.swift:490:24:490:31 | call to source() | semmle.label | call to source() | +| test.swift:492:14:492:32 | call to mkMyEnum2(_:) [mySingle:0] | semmle.label | call to mkMyEnum2(_:) [mySingle:0] | +| test.swift:492:24:492:31 | call to source() | semmle.label | call to source() | +| test.swift:494:13:494:35 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | +| test.swift:494:33:494:33 | d2 | semmle.label | d2 | +| test.swift:494:54:494:54 | d2 | semmle.label | d2 | +| test.swift:496:13:496:35 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | +| test.swift:496:33:496:33 | d4 | semmle.label | d4 | +| test.swift:496:54:496:54 | d4 | semmle.label | d4 | +| test.swift:498:13:498:35 | .mySingle(...) [mySingle:0] | semmle.label | .mySingle(...) [mySingle:0] | +| test.swift:498:33:498:33 | d6 | semmle.label | d6 | +| test.swift:498:54:498:54 | d6 | semmle.label | d6 | +| test.swift:501:14:501:36 | call to ... [some:0] | semmle.label | call to ... [some:0] | +| test.swift:501:28:501:35 | call to source() | semmle.label | call to source() | +| test.swift:503:14:503:34 | call to mkOptional1(_:) [some:0] | semmle.label | call to mkOptional1(_:) [some:0] | +| test.swift:503:26:503:33 | call to source() | semmle.label | call to source() | +| test.swift:505:14:505:34 | call to mkOptional2(_:) [some:0] | semmle.label | call to mkOptional2(_:) [some:0] | +| test.swift:505:26:505:33 | call to source() | semmle.label | call to source() | +| test.swift:507:15:507:15 | e2 [some:0] | semmle.label | e2 [some:0] | +| test.swift:507:15:507:17 | ...! | semmle.label | ...! | +| test.swift:509:15:509:15 | e4 [some:0] | semmle.label | e4 [some:0] | +| test.swift:509:15:509:17 | ...! | semmle.label | ...! | +| test.swift:511:15:511:15 | e6 [some:0] | semmle.label | e6 [some:0] | +| test.swift:511:15:511:17 | ...! | semmle.label | ...! | +| test.swift:517:13:517:28 | call to optionalSource() [some:0] | semmle.label | call to optionalSource() [some:0] | +| test.swift:519:8:519:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| test.swift:519:12:519:12 | a | semmle.label | a | +| test.swift:520:19:520:19 | a | semmle.label | a | +| test.swift:524:18:524:23 | (...) [Tuple element at index 0, some:0] | semmle.label | (...) [Tuple element at index 0, some:0] | +| test.swift:524:19:524:19 | x [some:0] | semmle.label | x [some:0] | +| test.swift:526:10:526:37 | (...) [Tuple element at index 0, some:0] | semmle.label | (...) [Tuple element at index 0, some:0] | +| test.swift:526:11:526:22 | .some(...) [some:0] | semmle.label | .some(...) [some:0] | +| test.swift:526:21:526:21 | a | semmle.label | a | +| test.swift:527:19:527:19 | a | semmle.label | a | +| test.swift:540:9:540:9 | self [x, some:0] | semmle.label | self [x, some:0] | +| test.swift:540:9:540:9 | value [some:0] | semmle.label | value [some:0] | +| test.swift:544:13:544:28 | call to optionalSource() [some:0] | semmle.label | call to optionalSource() [some:0] | +| test.swift:546:5:546:5 | [post] cx [x, some:0] | semmle.label | [post] cx [x, some:0] | +| test.swift:546:12:546:12 | x [some:0] | semmle.label | x [some:0] | +| test.swift:550:11:550:15 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| test.swift:550:15:550:15 | z1 | semmle.label | z1 | +| test.swift:550:20:550:20 | cx [x, some:0] | semmle.label | cx [x, some:0] | +| test.swift:550:20:550:23 | .x [some:0] | semmle.label | .x [some:0] | +| test.swift:551:15:551:15 | z1 | semmle.label | z1 | +| test.swift:557:13:557:21 | call to +(_:) | semmle.label | call to +(_:) | +| test.swift:557:14:557:21 | call to source() | semmle.label | call to source() | +| test.swift:558:14:558:21 | call to source() | semmle.label | call to source() | +| test.swift:566:9:566:9 | self [str] | semmle.label | self [str] | +| test.swift:567:5:569:5 | self[return] [str] | semmle.label | self[return] [str] | +| test.swift:567:10:567:13 | s | semmle.label | s | +| test.swift:568:7:568:7 | [post] self [str] | semmle.label | [post] self [str] | +| test.swift:568:13:568:13 | s | semmle.label | s | +| test.swift:573:17:576:5 | self[return] [str] | semmle.label | self[return] [str] | +| test.swift:574:7:574:7 | [post] self [str] | semmle.label | [post] self [str] | +| test.swift:574:20:574:28 | call to source3() | semmle.label | call to source3() | +| test.swift:575:17:575:17 | .str | semmle.label | .str | +| test.swift:575:17:575:17 | self [str] | semmle.label | self [str] | +| test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | semmle.label | call to MyClass.init(s:) [str] | +| test.swift:580:13:580:35 | .str | semmle.label | .str | +| test.swift:580:24:580:32 | call to source3() | semmle.label | call to source3() | +| test.swift:581:13:581:41 | call to MyClass.init(contentsOfFile:) [str] | semmle.label | call to MyClass.init(contentsOfFile:) [str] | +| test.swift:581:13:581:43 | .str | semmle.label | .str | +| test.swift:598:3:600:3 | self[return] [x] | semmle.label | self[return] [x] | +| test.swift:598:8:598:11 | x | semmle.label | x | +| test.swift:599:5:599:5 | [post] self [x] | semmle.label | [post] self [x] | +| test.swift:599:14:599:14 | x | semmle.label | x | +| test.swift:604:11:604:24 | call to S.init(x:) [x] | semmle.label | call to S.init(x:) [x] | +| test.swift:604:16:604:23 | call to source() | semmle.label | call to source() | +| test.swift:605:11:605:14 | enter #keyPath(...) [x] | semmle.label | enter #keyPath(...) [x] | +| test.swift:605:11:605:14 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | +| test.swift:605:14:605:14 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | +| test.swift:606:13:606:13 | s [x] | semmle.label | s [x] | +| test.swift:606:13:606:25 | \\...[...] | semmle.label | \\...[...] | +| test.swift:608:36:608:38 | enter #keyPath(...) [x] | semmle.label | enter #keyPath(...) [x] | +| test.swift:608:36:608:38 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | +| test.swift:608:38:608:38 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | +| test.swift:609:13:609:13 | s [x] | semmle.label | s [x] | +| test.swift:609:13:609:32 | \\...[...] | semmle.label | \\...[...] | +| test.swift:615:3:617:3 | self[return] [s, x] | semmle.label | self[return] [s, x] | +| test.swift:615:8:615:11 | s [x] | semmle.label | s [x] | +| test.swift:616:5:616:5 | [post] self [s, x] | semmle.label | [post] self [s, x] | +| test.swift:616:14:616:14 | s [x] | semmle.label | s [x] | +| test.swift:621:11:621:24 | call to S.init(x:) [x] | semmle.label | call to S.init(x:) [x] | +| test.swift:621:16:621:23 | call to source() | semmle.label | call to source() | +| test.swift:622:12:622:19 | call to S2.init(s:) [s, x] | semmle.label | call to S2.init(s:) [s, x] | +| test.swift:622:18:622:18 | s [x] | semmle.label | s [x] | +| test.swift:623:11:623:17 | enter #keyPath(...) [s, x] | semmle.label | enter #keyPath(...) [s, x] | +| test.swift:623:11:623:17 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | +| test.swift:623:15:623:15 | KeyPathComponent [s, x] | semmle.label | KeyPathComponent [s, x] | +| test.swift:623:17:623:17 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] | +| test.swift:624:13:624:13 | s2 [s, x] | semmle.label | s2 [s, x] | +| test.swift:624:13:624:26 | \\...[...] | semmle.label | \\...[...] | +| test.swift:628:17:628:26 | [...] [Array element] | semmle.label | [...] [Array element] | +| test.swift:628:18:628:25 | call to source() | semmle.label | call to source() | +| test.swift:629:13:629:22 | enter #keyPath(...) [Array element] | semmle.label | enter #keyPath(...) [Array element] | +| test.swift:629:13:629:22 | exit #keyPath(...) | semmle.label | exit #keyPath(...) | +| test.swift:629:20:629:22 | KeyPathComponent [Array element] | semmle.label | KeyPathComponent [Array element] | +| test.swift:630:15:630:15 | array [Array element] | semmle.label | array [Array element] | +| test.swift:630:15:630:31 | \\...[...] | semmle.label | \\...[...] | +| test.swift:649:13:649:20 | call to source() | semmle.label | call to source() | +| test.swift:657:15:657:15 | y | semmle.label | y | +| test.swift:659:9:659:16 | call to source() | semmle.label | call to source() | +| test.swift:661:11:661:11 | x | semmle.label | x | +| test.swift:661:15:661:15 | [post] y | semmle.label | [post] y | +| test.swift:662:15:662:15 | x | semmle.label | x | +| test.swift:663:15:663:15 | y | semmle.label | y | +| test.swift:669:5:669:5 | [post] arr1 [Array element] | semmle.label | [post] arr1 [Array element] | +| test.swift:669:15:669:22 | call to source() | semmle.label | call to source() | +| test.swift:670:15:670:15 | arr1 [Array element] | semmle.label | arr1 [Array element] | +| test.swift:670:15:670:21 | ...[...] | semmle.label | ...[...] | +| test.swift:673:16:673:25 | [...] [Array element] | semmle.label | [...] [Array element] | +| test.swift:673:17:673:24 | call to source() | semmle.label | call to source() | +| test.swift:674:15:674:15 | arr2 [Array element] | semmle.label | arr2 [Array element] | +| test.swift:674:15:674:21 | ...[...] | semmle.label | ...[...] | +| test.swift:676:18:676:29 | [...] [Array element, Array element] | semmle.label | [...] [Array element, Array element] | +| test.swift:676:19:676:28 | [...] [Array element] | semmle.label | [...] [Array element] | +| test.swift:676:20:676:27 | call to source() | semmle.label | call to source() | +| test.swift:678:15:678:15 | matrix [Array element, Array element] | semmle.label | matrix [Array element, Array element] | +| test.swift:678:15:678:23 | ...[...] [Array element] | semmle.label | ...[...] [Array element] | +| test.swift:678:15:678:26 | ...[...] | semmle.label | ...[...] | +| test.swift:681:5:681:5 | [post] matrix2 [Array element, Array element] | semmle.label | [post] matrix2 [Array element, Array element] | +| test.swift:681:5:681:14 | [post] getter for ...[...] [Array element] | semmle.label | [post] getter for ...[...] [Array element] | +| test.swift:681:21:681:28 | call to source() | semmle.label | call to source() | +| test.swift:682:15:682:15 | matrix2 [Array element, Array element] | semmle.label | matrix2 [Array element, Array element] | +| test.swift:682:15:682:24 | ...[...] [Array element] | semmle.label | ...[...] [Array element] | +| test.swift:682:15:682:27 | ...[...] | semmle.label | ...[...] | +| test.swift:693:5:693:5 | [post] arr6 [Array element] | semmle.label | [post] arr6 [Array element] | +| test.swift:693:17:693:24 | call to source() | semmle.label | call to source() | +| test.swift:694:15:694:15 | arr6 [Array element] | semmle.label | arr6 [Array element] | +| test.swift:694:15:694:21 | ...[...] | semmle.label | ...[...] | subpaths -| test.swift:75:21:75:22 | &... | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:31:75:32 | [post] &... | +| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y | | test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... | | test.swift:114:19:114:19 | arg | test.swift:123:10:123:13 | i | test.swift:124:16:124:16 | i | test.swift:114:12:114:22 | call to ... | | test.swift:119:31:119:31 | x | test.swift:113:14:113:19 | arg | test.swift:114:12:114:22 | call to ... | test.swift:119:18:119:44 | call to forward(arg:lambda:) | @@ -600,18 +732,21 @@ subpaths | test.swift:218:11:218:18 | call to source() | test.swift:169:12:169:22 | value | test.swift:170:5:170:5 | [post] self [x] | test.swift:218:3:218:5 | [post] getter for .a [x] | | test.swift:219:13:219:13 | b [a, x] | test.swift:185:7:185:7 | self [a, x] | file://:0:0:0:0 | .a [x] | test.swift:219:13:219:15 | .a [x] | | test.swift:219:13:219:15 | .a [x] | test.swift:163:7:163:7 | self [x] | file://:0:0:0:0 | .x | test.swift:219:13:219:17 | .x | -| test.swift:515:12:515:12 | x [some:0] | test.swift:509:9:509:9 | value [some:0] | file://:0:0:0:0 | [post] self [x, some:0] | test.swift:515:5:515:5 | [post] cx [x, some:0] | -| test.swift:519:20:519:20 | cx [x, some:0] | test.swift:509:9:509:9 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] | test.swift:519:20:519:23 | .x [some:0] | -| test.swift:543:20:543:28 | call to source3() | test.swift:536:10:536:13 | s | test.swift:537:7:537:7 | [post] self [str] | test.swift:543:7:543:7 | [post] self [str] | -| test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | test.swift:535:9:535:9 | self [str] | file://:0:0:0:0 | .str | test.swift:549:13:549:35 | .str | -| test.swift:549:24:549:32 | call to source3() | test.swift:536:10:536:13 | s | test.swift:536:5:538:5 | self[return] [str] | test.swift:549:13:549:33 | call to MyClass.init(s:) [str] | -| test.swift:550:13:550:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:535:9:535:9 | self [str] | file://:0:0:0:0 | .str | test.swift:550:13:550:43 | .str | -| test.swift:573:16:573:23 | call to source() | test.swift:567:8:567:11 | x | test.swift:567:3:569:3 | self[return] [x] | test.swift:573:11:573:24 | call to S.init(x:) [x] | -| test.swift:575:13:575:13 | s [x] | test.swift:574:11:574:14 | enter #keyPath(...) [x] | test.swift:574:11:574:14 | exit #keyPath(...) | test.swift:575:13:575:25 | \\...[...] | -| test.swift:578:13:578:13 | s [x] | test.swift:577:36:577:38 | enter #keyPath(...) [x] | test.swift:577:36:577:38 | exit #keyPath(...) | test.swift:578:13:578:32 | \\...[...] | -| test.swift:590:16:590:23 | call to source() | test.swift:567:8:567:11 | x | test.swift:567:3:569:3 | self[return] [x] | test.swift:590:11:590:24 | call to S.init(x:) [x] | -| test.swift:591:18:591:18 | s [x] | test.swift:584:8:584:11 | s [x] | test.swift:584:3:586:3 | self[return] [s, x] | test.swift:591:12:591:19 | call to S2.init(s:) [s, x] | -| test.swift:593:13:593:13 | s2 [s, x] | test.swift:592:11:592:17 | enter #keyPath(...) [s, x] | test.swift:592:11:592:17 | exit #keyPath(...) | test.swift:593:13:593:26 | \\...[...] | +| test.swift:490:24:490:31 | call to source() | test.swift:375:16:375:21 | v | test.swift:375:45:375:62 | call to ... [mySingle:0] | test.swift:490:14:490:32 | call to mkMyEnum1(_:) [mySingle:0] | +| test.swift:503:26:503:33 | call to source() | test.swift:377:18:377:23 | v | test.swift:377:45:377:60 | call to ... [some:0] | test.swift:503:14:503:34 | call to mkOptional1(_:) [some:0] | +| test.swift:546:12:546:12 | x [some:0] | test.swift:540:9:540:9 | value [some:0] | file://:0:0:0:0 | [post] self [x, some:0] | test.swift:546:5:546:5 | [post] cx [x, some:0] | +| test.swift:550:20:550:20 | cx [x, some:0] | test.swift:540:9:540:9 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] | test.swift:550:20:550:23 | .x [some:0] | +| test.swift:574:20:574:28 | call to source3() | test.swift:567:10:567:13 | s | test.swift:568:7:568:7 | [post] self [str] | test.swift:574:7:574:7 | [post] self [str] | +| test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | test.swift:566:9:566:9 | self [str] | file://:0:0:0:0 | .str | test.swift:580:13:580:35 | .str | +| test.swift:580:24:580:32 | call to source3() | test.swift:567:10:567:13 | s | test.swift:567:5:569:5 | self[return] [str] | test.swift:580:13:580:33 | call to MyClass.init(s:) [str] | +| test.swift:581:13:581:41 | call to MyClass.init(contentsOfFile:) [str] | test.swift:566:9:566:9 | self [str] | file://:0:0:0:0 | .str | test.swift:581:13:581:43 | .str | +| test.swift:604:16:604:23 | call to source() | test.swift:598:8:598:11 | x | test.swift:598:3:600:3 | self[return] [x] | test.swift:604:11:604:24 | call to S.init(x:) [x] | +| test.swift:606:13:606:13 | s [x] | test.swift:605:11:605:14 | enter #keyPath(...) [x] | test.swift:605:11:605:14 | exit #keyPath(...) | test.swift:606:13:606:25 | \\...[...] | +| test.swift:609:13:609:13 | s [x] | test.swift:608:36:608:38 | enter #keyPath(...) [x] | test.swift:608:36:608:38 | exit #keyPath(...) | test.swift:609:13:609:32 | \\...[...] | +| test.swift:621:16:621:23 | call to source() | test.swift:598:8:598:11 | x | test.swift:598:3:600:3 | self[return] [x] | test.swift:621:11:621:24 | call to S.init(x:) [x] | +| test.swift:622:18:622:18 | s [x] | test.swift:615:8:615:11 | s [x] | test.swift:615:3:617:3 | self[return] [s, x] | test.swift:622:12:622:19 | call to S2.init(s:) [s, x] | +| test.swift:624:13:624:13 | s2 [s, x] | test.swift:623:11:623:17 | enter #keyPath(...) [s, x] | test.swift:623:11:623:17 | exit #keyPath(...) | test.swift:624:13:624:26 | \\...[...] | +| test.swift:630:15:630:15 | array [Array element] | test.swift:629:13:629:22 | enter #keyPath(...) [Array element] | test.swift:629:13:629:22 | exit #keyPath(...) | test.swift:630:15:630:31 | \\...[...] | #select | test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 | result | | test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:9:15:9:15 | t1 | result | @@ -665,26 +800,38 @@ subpaths | test.swift:361:15:361:18 | .1 | test.swift:351:31:351:38 | call to source() | test.swift:361:15:361:18 | .1 | result | | test.swift:363:15:363:15 | a | test.swift:351:18:351:25 | call to source() | test.swift:363:15:363:15 | a | result | | test.swift:364:15:364:15 | b | test.swift:351:31:351:38 | call to source() | test.swift:364:15:364:15 | b | result | -| test.swift:404:19:404:19 | a | test.swift:398:19:398:26 | call to source() | test.swift:404:19:404:19 | a | result | -| test.swift:413:19:413:19 | x | test.swift:398:19:398:26 | call to source() | test.swift:413:19:413:19 | x | result | -| test.swift:429:19:429:19 | b | test.swift:420:26:420:33 | call to source() | test.swift:429:19:429:19 | b | result | -| test.swift:439:19:439:19 | y | test.swift:420:26:420:33 | call to source() | test.swift:439:19:439:19 | y | result | -| test.swift:455:19:455:19 | c | test.swift:420:26:420:33 | call to source() | test.swift:455:19:455:19 | c | result | -| test.swift:464:19:464:19 | x | test.swift:463:51:463:58 | call to source() | test.swift:464:19:464:19 | x | result | -| test.swift:468:19:468:19 | c | test.swift:420:26:420:33 | call to source() | test.swift:468:19:468:19 | c | result | -| test.swift:474:19:474:19 | b | test.swift:420:26:420:33 | call to source() | test.swift:474:19:474:19 | b | result | -| test.swift:477:19:477:19 | e | test.swift:420:26:420:33 | call to source() | test.swift:477:19:477:19 | e | result | -| test.swift:489:19:489:19 | a | test.swift:259:12:259:19 | call to source() | test.swift:489:19:489:19 | a | result | -| test.swift:496:19:496:19 | a | test.swift:259:12:259:19 | call to source() | test.swift:496:19:496:19 | a | result | -| test.swift:520:15:520:15 | z1 | test.swift:259:12:259:19 | call to source() | test.swift:520:15:520:15 | z1 | result | -| test.swift:526:13:526:21 | call to +(_:) | test.swift:526:14:526:21 | call to source() | test.swift:526:13:526:21 | call to +(_:) | result | -| test.swift:527:14:527:21 | call to source() | test.swift:527:14:527:21 | call to source() | test.swift:527:14:527:21 | call to source() | result | -| test.swift:544:17:544:17 | .str | test.swift:543:20:543:28 | call to source3() | test.swift:544:17:544:17 | .str | result | -| test.swift:549:13:549:35 | .str | test.swift:549:24:549:32 | call to source3() | test.swift:549:13:549:35 | .str | result | -| test.swift:550:13:550:43 | .str | test.swift:543:20:543:28 | call to source3() | test.swift:550:13:550:43 | .str | result | -| test.swift:575:13:575:25 | \\...[...] | test.swift:573:16:573:23 | call to source() | test.swift:575:13:575:25 | \\...[...] | result | -| test.swift:578:13:578:32 | \\...[...] | test.swift:573:16:573:23 | call to source() | test.swift:578:13:578:32 | \\...[...] | result | -| test.swift:593:13:593:26 | \\...[...] | test.swift:590:16:590:23 | call to source() | test.swift:593:13:593:26 | \\...[...] | result | -| test.swift:626:15:626:15 | y | test.swift:618:13:618:20 | call to source() | test.swift:626:15:626:15 | y | result | -| test.swift:631:15:631:15 | x | test.swift:628:9:628:16 | call to source() | test.swift:631:15:631:15 | x | result | -| test.swift:632:15:632:15 | y | test.swift:628:9:628:16 | call to source() | test.swift:632:15:632:15 | y | result | +| test.swift:409:19:409:19 | a | test.swift:403:19:403:26 | call to source() | test.swift:409:19:409:19 | a | result | +| test.swift:418:19:418:19 | x | test.swift:403:19:403:26 | call to source() | test.swift:418:19:418:19 | x | result | +| test.swift:434:19:434:19 | b | test.swift:425:26:425:33 | call to source() | test.swift:434:19:434:19 | b | result | +| test.swift:444:19:444:19 | y | test.swift:425:26:425:33 | call to source() | test.swift:444:19:444:19 | y | result | +| test.swift:460:19:460:19 | c | test.swift:425:26:425:33 | call to source() | test.swift:460:19:460:19 | c | result | +| test.swift:469:19:469:19 | x | test.swift:468:51:468:58 | call to source() | test.swift:469:19:469:19 | x | result | +| test.swift:473:19:473:19 | c | test.swift:425:26:425:33 | call to source() | test.swift:473:19:473:19 | c | result | +| test.swift:479:19:479:19 | b | test.swift:425:26:425:33 | call to source() | test.swift:479:19:479:19 | b | result | +| test.swift:482:19:482:19 | e | test.swift:425:26:425:33 | call to source() | test.swift:482:19:482:19 | e | result | +| test.swift:494:54:494:54 | d2 | test.swift:488:30:488:37 | call to source() | test.swift:494:54:494:54 | d2 | result | +| test.swift:496:54:496:54 | d4 | test.swift:490:24:490:31 | call to source() | test.swift:496:54:496:54 | d4 | result | +| test.swift:498:54:498:54 | d6 | test.swift:492:24:492:31 | call to source() | test.swift:498:54:498:54 | d6 | result | +| test.swift:507:15:507:17 | ...! | test.swift:501:28:501:35 | call to source() | test.swift:507:15:507:17 | ...! | result | +| test.swift:509:15:509:17 | ...! | test.swift:503:26:503:33 | call to source() | test.swift:509:15:509:17 | ...! | result | +| test.swift:511:15:511:17 | ...! | test.swift:505:26:505:33 | call to source() | test.swift:511:15:511:17 | ...! | result | +| test.swift:520:19:520:19 | a | test.swift:259:12:259:19 | call to source() | test.swift:520:19:520:19 | a | result | +| test.swift:527:19:527:19 | a | test.swift:259:12:259:19 | call to source() | test.swift:527:19:527:19 | a | result | +| test.swift:551:15:551:15 | z1 | test.swift:259:12:259:19 | call to source() | test.swift:551:15:551:15 | z1 | result | +| test.swift:557:13:557:21 | call to +(_:) | test.swift:557:14:557:21 | call to source() | test.swift:557:13:557:21 | call to +(_:) | result | +| test.swift:558:14:558:21 | call to source() | test.swift:558:14:558:21 | call to source() | test.swift:558:14:558:21 | call to source() | result | +| test.swift:575:17:575:17 | .str | test.swift:574:20:574:28 | call to source3() | test.swift:575:17:575:17 | .str | result | +| test.swift:580:13:580:35 | .str | test.swift:580:24:580:32 | call to source3() | test.swift:580:13:580:35 | .str | result | +| test.swift:581:13:581:43 | .str | test.swift:574:20:574:28 | call to source3() | test.swift:581:13:581:43 | .str | result | +| test.swift:606:13:606:25 | \\...[...] | test.swift:604:16:604:23 | call to source() | test.swift:606:13:606:25 | \\...[...] | result | +| test.swift:609:13:609:32 | \\...[...] | test.swift:604:16:604:23 | call to source() | test.swift:609:13:609:32 | \\...[...] | result | +| test.swift:624:13:624:26 | \\...[...] | test.swift:621:16:621:23 | call to source() | test.swift:624:13:624:26 | \\...[...] | result | +| test.swift:630:15:630:31 | \\...[...] | test.swift:628:18:628:25 | call to source() | test.swift:630:15:630:31 | \\...[...] | result | +| test.swift:657:15:657:15 | y | test.swift:649:13:649:20 | call to source() | test.swift:657:15:657:15 | y | result | +| test.swift:662:15:662:15 | x | test.swift:659:9:659:16 | call to source() | test.swift:662:15:662:15 | x | result | +| test.swift:663:15:663:15 | y | test.swift:659:9:659:16 | call to source() | test.swift:663:15:663:15 | y | result | +| test.swift:670:15:670:21 | ...[...] | test.swift:669:15:669:22 | call to source() | test.swift:670:15:670:21 | ...[...] | result | +| test.swift:674:15:674:21 | ...[...] | test.swift:673:17:673:24 | call to source() | test.swift:674:15:674:21 | ...[...] | result | +| test.swift:678:15:678:26 | ...[...] | test.swift:676:20:676:27 | call to source() | test.swift:678:15:678:26 | ...[...] | result | +| test.swift:682:15:682:27 | ...[...] | test.swift:681:21:681:28 | call to source() | test.swift:682:15:682:27 | ...[...] | result | +| test.swift:694:15:694:21 | ...[...] | test.swift:693:17:693:24 | call to source() | test.swift:694:15:694:21 | ...[...] | result | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll b/swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll index 853e8ac3be7..952035f7c77 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll +++ b/swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll @@ -5,6 +5,7 @@ import swift import codeql.swift.dataflow.DataFlow import codeql.swift.dataflow.ExternalFlow +import codeql.swift.frameworks.Frameworks module TestConfiguration implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { @@ -21,8 +22,14 @@ module TestConfiguration implements DataFlow::ConfigSig { private class TestSummaries extends SummaryModelCsv { override predicate row(string row) { - // model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`). - row = ";Int;true;signum();;;Argument[-1];ReturnValue;value" + row = + [ + // model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`). + ";Int;true;signum();;;Argument[-1];ReturnValue;value", + // test Enum content in MAD + ";;false;mkMyEnum2(_:);;;Argument[0];ReturnValue.EnumElement[mySingle:0];value", + ";;false;mkOptional2(_:);;;Argument[0];ReturnValue.OptionalSome;value" + ] } } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 3294416283e..99e9ceb5ba8 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -41,7 +41,7 @@ | test.swift:60:15:60:15 | [post] x | test.swift:61:23:61:23 | x | | test.swift:60:15:60:15 | x | test.swift:61:23:61:23 | x | | test.swift:61:22:61:23 | &... | test.swift:62:15:62:15 | x | -| test.swift:61:22:61:23 | [post] &... | test.swift:62:15:62:15 | x | +| test.swift:61:23:61:23 | [post] x | test.swift:61:22:61:23 | &... | | test.swift:61:23:61:23 | x | test.swift:61:22:61:23 | &... | | test.swift:65:16:65:28 | SSA def(arg1) | test.swift:66:21:66:21 | arg1 | | test.swift:65:16:65:28 | arg1 | test.swift:65:16:65:28 | SSA def(arg1) | @@ -64,10 +64,10 @@ | test.swift:74:9:74:12 | ... as ... | test.swift:74:9:74:9 | y | | test.swift:74:18:74:18 | 0 | test.swift:74:9:74:12 | ... as ... | | test.swift:75:21:75:22 | &... | test.swift:76:15:76:15 | x | -| test.swift:75:21:75:22 | [post] &... | test.swift:76:15:76:15 | x | +| test.swift:75:22:75:22 | [post] x | test.swift:75:21:75:22 | &... | | test.swift:75:22:75:22 | x | test.swift:75:21:75:22 | &... | | test.swift:75:31:75:32 | &... | test.swift:77:15:77:15 | y | -| test.swift:75:31:75:32 | [post] &... | test.swift:77:15:77:15 | y | +| test.swift:75:32:75:32 | [post] y | test.swift:75:31:75:32 | &... | | test.swift:75:32:75:32 | y | test.swift:75:31:75:32 | &... | | test.swift:81:5:81:18 | SSA def(arg) | test.swift:80:1:82:1 | arg[return] | | test.swift:81:11:81:18 | call to source() | test.swift:81:5:81:18 | SSA def(arg) | @@ -87,7 +87,7 @@ | test.swift:96:19:96:19 | [post] x | test.swift:97:40:97:40 | x | | test.swift:96:19:96:19 | x | test.swift:97:40:97:40 | x | | test.swift:97:39:97:40 | &... | test.swift:98:19:98:19 | x | -| test.swift:97:39:97:40 | [post] &... | test.swift:98:19:98:19 | x | +| test.swift:97:40:97:40 | [post] x | test.swift:97:39:97:40 | &... | | test.swift:97:40:97:40 | x | test.swift:97:39:97:40 | &... | | test.swift:102:13:102:13 | SSA def(x) | test.swift:103:19:103:19 | x | | test.swift:102:13:102:13 | x | test.swift:102:13:102:13 | SSA def(x) | @@ -96,7 +96,7 @@ | test.swift:103:19:103:19 | [post] x | test.swift:104:41:104:41 | x | | test.swift:103:19:103:19 | x | test.swift:104:41:104:41 | x | | test.swift:104:40:104:41 | &... | test.swift:105:19:105:19 | x | -| test.swift:104:40:104:41 | [post] &... | test.swift:105:19:105:19 | x | +| test.swift:104:41:104:41 | [post] x | test.swift:104:40:104:41 | &... | | test.swift:104:41:104:41 | x | test.swift:104:40:104:41 | &... | | test.swift:109:9:109:14 | SSA def(arg) | test.swift:110:12:110:12 | arg | | test.swift:109:9:109:14 | arg | test.swift:109:9:109:14 | SSA def(arg) | @@ -374,314 +374,440 @@ | test.swift:360:15:360:15 | t2 | test.swift:361:15:361:15 | t2 | | test.swift:361:15:361:15 | [post] t2 | test.swift:362:15:362:15 | t2 | | test.swift:361:15:361:15 | t2 | test.swift:362:15:362:15 | t2 | -| test.swift:376:9:376:9 | SSA def(a) | test.swift:378:12:378:12 | a | -| test.swift:376:9:376:9 | a | test.swift:376:9:376:9 | SSA def(a) | -| test.swift:376:9:376:13 | ... as ... | test.swift:376:9:376:9 | a | -| test.swift:376:22:376:23 | .myNone | test.swift:376:9:376:13 | ... as ... | -| test.swift:378:12:378:12 | a | test.swift:379:10:379:11 | .myNone | -| test.swift:378:12:378:12 | a | test.swift:381:10:381:25 | .mySingle(...) | -| test.swift:378:12:378:12 | a | test.swift:383:10:383:30 | .myPair(...) | -| test.swift:378:12:378:12 | a | test.swift:386:14:386:26 | .myCons(...) | -| test.swift:378:12:378:12 | a | test.swift:390:32:390:32 | a | -| test.swift:381:24:381:24 | SSA def(a) | test.swift:382:19:382:19 | a | -| test.swift:381:24:381:24 | a | test.swift:381:24:381:24 | SSA def(a) | -| test.swift:383:22:383:22 | SSA def(a) | test.swift:384:19:384:19 | a | -| test.swift:383:22:383:22 | a | test.swift:383:22:383:22 | SSA def(a) | -| test.swift:383:29:383:29 | SSA def(b) | test.swift:385:19:385:19 | b | -| test.swift:383:29:383:29 | b | test.swift:383:29:383:29 | SSA def(b) | -| test.swift:386:10:386:26 | SSA phi(a) | test.swift:387:19:387:19 | a | -| test.swift:386:22:386:22 | SSA def(a) | test.swift:386:10:386:26 | SSA phi(a) | -| test.swift:386:22:386:22 | a | test.swift:386:22:386:22 | SSA def(a) | -| test.swift:390:27:390:27 | SSA def(x) | test.swift:391:19:391:19 | x | -| test.swift:390:27:390:27 | x | test.swift:390:27:390:27 | SSA def(x) | -| test.swift:390:32:390:32 | a | test.swift:390:13:390:28 | .mySingle(...) | -| test.swift:390:32:390:32 | a | test.swift:393:37:393:37 | a | -| test.swift:393:25:393:25 | SSA def(x) | test.swift:394:19:394:19 | x | -| test.swift:393:25:393:25 | x | test.swift:393:25:393:25 | SSA def(x) | -| test.swift:393:32:393:32 | SSA def(y) | test.swift:395:19:395:19 | y | -| test.swift:393:32:393:32 | y | test.swift:393:32:393:32 | SSA def(y) | -| test.swift:393:37:393:37 | a | test.swift:393:13:393:33 | .myPair(...) | -| test.swift:398:5:398:27 | SSA def(a) | test.swift:400:12:400:12 | a | -| test.swift:398:9:398:27 | call to ... | test.swift:398:5:398:27 | SSA def(a) | -| test.swift:400:12:400:12 | a | test.swift:401:10:401:11 | .myNone | -| test.swift:400:12:400:12 | a | test.swift:403:10:403:25 | .mySingle(...) | -| test.swift:400:12:400:12 | a | test.swift:405:10:405:30 | .myPair(...) | -| test.swift:400:12:400:12 | a | test.swift:408:14:408:26 | .myCons(...) | -| test.swift:400:12:400:12 | a | test.swift:412:32:412:32 | a | -| test.swift:403:24:403:24 | SSA def(a) | test.swift:404:19:404:19 | a | -| test.swift:403:24:403:24 | a | test.swift:403:24:403:24 | SSA def(a) | -| test.swift:405:22:405:22 | SSA def(a) | test.swift:406:19:406:19 | a | -| test.swift:405:22:405:22 | a | test.swift:405:22:405:22 | SSA def(a) | -| test.swift:405:29:405:29 | SSA def(b) | test.swift:407:19:407:19 | b | -| test.swift:405:29:405:29 | b | test.swift:405:29:405:29 | SSA def(b) | -| test.swift:408:10:408:26 | SSA phi(a) | test.swift:409:19:409:19 | a | -| test.swift:408:22:408:22 | SSA def(a) | test.swift:408:10:408:26 | SSA phi(a) | -| test.swift:408:22:408:22 | a | test.swift:408:22:408:22 | SSA def(a) | -| test.swift:412:27:412:27 | SSA def(x) | test.swift:413:19:413:19 | x | -| test.swift:412:27:412:27 | x | test.swift:412:27:412:27 | SSA def(x) | -| test.swift:412:32:412:32 | a | test.swift:412:13:412:28 | .mySingle(...) | -| test.swift:412:32:412:32 | a | test.swift:415:37:415:37 | a | -| test.swift:415:25:415:25 | SSA def(x) | test.swift:416:19:416:19 | x | -| test.swift:415:25:415:25 | x | test.swift:415:25:415:25 | SSA def(x) | -| test.swift:415:32:415:32 | SSA def(y) | test.swift:417:19:417:19 | y | -| test.swift:415:32:415:32 | y | test.swift:415:32:415:32 | SSA def(y) | -| test.swift:415:37:415:37 | a | test.swift:415:13:415:33 | .myPair(...) | -| test.swift:420:5:420:34 | SSA def(a) | test.swift:422:12:422:12 | a | -| test.swift:420:9:420:34 | call to ... | test.swift:420:5:420:34 | SSA def(a) | -| test.swift:422:12:422:12 | a | test.swift:423:10:423:11 | .myNone | -| test.swift:422:12:422:12 | a | test.swift:425:10:425:25 | .mySingle(...) | -| test.swift:422:12:422:12 | a | test.swift:427:10:427:30 | .myPair(...) | -| test.swift:422:12:422:12 | a | test.swift:430:14:430:26 | .myCons(...) | -| test.swift:422:12:422:12 | a | test.swift:434:32:434:32 | a | -| test.swift:425:24:425:24 | SSA def(a) | test.swift:426:19:426:19 | a | -| test.swift:425:24:425:24 | a | test.swift:425:24:425:24 | SSA def(a) | -| test.swift:427:22:427:22 | SSA def(a) | test.swift:428:19:428:19 | a | -| test.swift:427:22:427:22 | a | test.swift:427:22:427:22 | SSA def(a) | -| test.swift:427:29:427:29 | SSA def(b) | test.swift:429:19:429:19 | b | -| test.swift:427:29:427:29 | b | test.swift:427:29:427:29 | SSA def(b) | -| test.swift:430:10:430:26 | SSA phi(a) | test.swift:431:19:431:19 | a | -| test.swift:430:22:430:22 | SSA def(a) | test.swift:430:10:430:26 | SSA phi(a) | -| test.swift:430:22:430:22 | a | test.swift:430:22:430:22 | SSA def(a) | -| test.swift:434:27:434:27 | SSA def(x) | test.swift:435:19:435:19 | x | -| test.swift:434:27:434:27 | x | test.swift:434:27:434:27 | SSA def(x) | -| test.swift:434:32:434:32 | a | test.swift:434:13:434:28 | .mySingle(...) | -| test.swift:434:32:434:32 | a | test.swift:437:37:437:37 | a | -| test.swift:437:25:437:25 | SSA def(x) | test.swift:438:19:438:19 | x | -| test.swift:437:25:437:25 | x | test.swift:437:25:437:25 | SSA def(x) | -| test.swift:437:32:437:32 | SSA def(y) | test.swift:439:19:439:19 | y | -| test.swift:437:32:437:32 | y | test.swift:437:32:437:32 | SSA def(y) | -| test.swift:437:37:437:37 | a | test.swift:437:13:437:33 | .myPair(...) | -| test.swift:437:37:437:37 | a | test.swift:442:33:442:33 | a | -| test.swift:442:9:442:9 | SSA def(b) | test.swift:444:12:444:12 | b | -| test.swift:442:9:442:9 | b | test.swift:442:9:442:9 | SSA def(b) | -| test.swift:442:9:442:12 | ... as ... | test.swift:442:9:442:9 | b | -| test.swift:442:21:442:34 | call to ... | test.swift:442:9:442:12 | ... as ... | -| test.swift:442:33:442:33 | [post] a | test.swift:471:13:471:13 | a | -| test.swift:442:33:442:33 | a | test.swift:471:13:471:13 | a | -| test.swift:444:12:444:12 | b | test.swift:445:10:445:11 | .myNone | -| test.swift:444:12:444:12 | b | test.swift:447:10:447:25 | .mySingle(...) | -| test.swift:444:12:444:12 | b | test.swift:449:10:449:30 | .myPair(...) | -| test.swift:444:12:444:12 | b | test.swift:452:14:452:38 | .myCons(...) | -| test.swift:444:12:444:12 | b | test.swift:456:14:456:26 | .myCons(...) | -| test.swift:444:12:444:12 | b | test.swift:467:45:467:45 | b | -| test.swift:447:24:447:24 | SSA def(a) | test.swift:448:19:448:19 | a | -| test.swift:447:24:447:24 | a | test.swift:447:24:447:24 | SSA def(a) | -| test.swift:449:22:449:22 | SSA def(a) | test.swift:450:19:450:19 | a | -| test.swift:449:22:449:22 | a | test.swift:449:22:449:22 | SSA def(a) | -| test.swift:449:29:449:29 | SSA def(b) | test.swift:451:19:451:19 | b | -| test.swift:449:29:449:29 | b | test.swift:449:29:449:29 | SSA def(b) | -| test.swift:452:10:452:38 | SSA phi(a) | test.swift:453:19:453:19 | a | -| test.swift:452:10:452:38 | SSA phi(b) | test.swift:454:19:454:19 | b | -| test.swift:452:10:452:38 | SSA phi(c) | test.swift:455:19:455:19 | c | -| test.swift:452:22:452:22 | SSA def(a) | test.swift:452:10:452:38 | SSA phi(a) | -| test.swift:452:22:452:22 | a | test.swift:452:22:452:22 | SSA def(a) | -| test.swift:452:33:452:33 | SSA def(b) | test.swift:452:10:452:38 | SSA phi(b) | -| test.swift:452:33:452:33 | b | test.swift:452:33:452:33 | SSA def(b) | -| test.swift:452:36:452:36 | SSA def(c) | test.swift:452:10:452:38 | SSA phi(c) | -| test.swift:452:36:452:36 | c | test.swift:452:36:452:36 | SSA def(c) | -| test.swift:456:10:456:26 | SSA phi(a) | test.swift:457:19:457:19 | a | -| test.swift:456:22:456:22 | SSA def(a) | test.swift:456:10:456:26 | SSA phi(a) | -| test.swift:456:22:456:22 | a | test.swift:456:22:456:22 | SSA def(a) | -| test.swift:460:27:460:27 | SSA def(x) | test.swift:461:19:461:19 | x | -| test.swift:460:27:460:27 | x | test.swift:460:27:460:27 | SSA def(x) | -| test.swift:460:32:460:57 | call to ... | test.swift:460:13:460:28 | .mySingle(...) | -| test.swift:463:31:463:31 | SSA def(x) | test.swift:464:19:464:19 | x | -| test.swift:463:31:463:31 | x | test.swift:463:31:463:31 | SSA def(x) | -| test.swift:463:38:463:38 | SSA def(y) | test.swift:465:19:465:19 | y | -| test.swift:463:38:463:38 | y | test.swift:463:38:463:38 | SSA def(y) | -| test.swift:463:43:463:62 | call to ... | test.swift:463:13:463:39 | .myPair(...) | -| test.swift:467:13:467:41 | SSA phi(c) | test.swift:468:19:468:19 | c | -| test.swift:467:39:467:39 | SSA def(c) | test.swift:467:13:467:41 | SSA phi(c) | -| test.swift:467:39:467:39 | c | test.swift:467:39:467:39 | SSA def(c) | -| test.swift:467:45:467:45 | b | test.swift:467:17:467:41 | .myCons(...) | -| test.swift:467:45:467:45 | b | test.swift:471:16:471:16 | b | -| test.swift:471:12:471:17 | (...) | test.swift:472:14:472:55 | (...) | -| test.swift:471:12:471:17 | (...) | test.swift:478:5:478:5 | _ | -| test.swift:472:10:472:55 | SSA phi(a) | test.swift:473:19:473:19 | a | -| test.swift:472:10:472:55 | SSA phi(b) | test.swift:474:19:474:19 | b | -| test.swift:472:10:472:55 | SSA phi(c) | test.swift:475:19:475:19 | c | -| test.swift:472:10:472:55 | SSA phi(d) | test.swift:476:19:476:19 | d | -| test.swift:472:10:472:55 | SSA phi(e) | test.swift:477:19:477:19 | e | -| test.swift:472:23:472:23 | SSA def(a) | test.swift:472:10:472:55 | SSA phi(a) | -| test.swift:472:23:472:23 | a | test.swift:472:23:472:23 | SSA def(a) | -| test.swift:472:26:472:26 | SSA def(b) | test.swift:472:10:472:55 | SSA phi(b) | -| test.swift:472:26:472:26 | b | test.swift:472:26:472:26 | SSA def(b) | -| test.swift:472:38:472:38 | SSA def(c) | test.swift:472:10:472:55 | SSA phi(c) | -| test.swift:472:38:472:38 | c | test.swift:472:38:472:38 | SSA def(c) | -| test.swift:472:49:472:49 | SSA def(d) | test.swift:472:10:472:55 | SSA phi(d) | -| test.swift:472:49:472:49 | d | test.swift:472:49:472:49 | SSA def(d) | -| test.swift:472:52:472:52 | SSA def(e) | test.swift:472:10:472:55 | SSA phi(e) | -| test.swift:472:52:472:52 | e | test.swift:472:52:472:52 | SSA def(e) | -| test.swift:485:21:485:27 | SSA def(y) | test.swift:488:27:488:27 | y | -| test.swift:485:21:485:27 | SSA def(y) | test.swift:493:22:493:22 | y | -| test.swift:485:21:485:27 | y | test.swift:485:21:485:27 | SSA def(y) | -| test.swift:486:9:486:9 | SSA def(x) | test.swift:488:16:488:16 | x | -| test.swift:486:9:486:9 | x | test.swift:486:9:486:9 | SSA def(x) | -| test.swift:486:13:486:28 | call to optionalSource() | test.swift:486:9:486:9 | x | -| test.swift:488:12:488:12 | SSA def(a) | test.swift:488:27:488:27 | SSA phi(a) | -| test.swift:488:12:488:12 | a | test.swift:488:12:488:12 | SSA def(a) | -| test.swift:488:16:488:16 | x | test.swift:488:8:488:12 | let ...? | -| test.swift:488:16:488:16 | x | test.swift:493:19:493:19 | x | -| test.swift:488:23:488:23 | SSA def(b) | test.swift:490:19:490:19 | b | -| test.swift:488:23:488:23 | b | test.swift:488:23:488:23 | SSA def(b) | -| test.swift:488:27:488:27 | SSA phi(a) | test.swift:489:19:489:19 | a | -| test.swift:488:27:488:27 | y | test.swift:488:19:488:23 | let ...? | -| test.swift:488:27:488:27 | y | test.swift:493:22:493:22 | y | -| test.swift:493:9:493:9 | SSA def(tuple1) | test.swift:494:12:494:12 | tuple1 | -| test.swift:493:9:493:9 | tuple1 | test.swift:493:9:493:9 | SSA def(tuple1) | -| test.swift:493:18:493:23 | (...) | test.swift:493:9:493:9 | tuple1 | -| test.swift:494:12:494:12 | tuple1 | test.swift:495:10:495:37 | (...) | -| test.swift:494:12:494:12 | tuple1 | test.swift:498:5:498:5 | _ | -| test.swift:495:21:495:21 | SSA def(a) | test.swift:496:19:496:19 | a | -| test.swift:495:21:495:21 | a | test.swift:495:21:495:21 | SSA def(a) | -| test.swift:495:35:495:35 | SSA def(b) | test.swift:497:19:497:19 | b | -| test.swift:495:35:495:35 | b | test.swift:495:35:495:35 | SSA def(b) | -| test.swift:502:13:502:13 | SSA def(x) | test.swift:503:19:503:19 | x | -| test.swift:502:13:502:13 | x | test.swift:502:13:502:13 | SSA def(x) | -| test.swift:502:16:502:16 | SSA def(y) | test.swift:504:19:504:19 | y | -| test.swift:502:16:502:16 | y | test.swift:502:16:502:16 | SSA def(y) | -| test.swift:502:21:502:29 | call to source2() | test.swift:502:8:502:17 | let ...? | -| test.swift:508:7:508:7 | SSA def(self) | test.swift:508:7:508:7 | self[return] | -| test.swift:508:7:508:7 | SSA def(self) | test.swift:508:7:508:7 | self[return] | -| test.swift:508:7:508:7 | self | test.swift:508:7:508:7 | SSA def(self) | -| test.swift:508:7:508:7 | self | test.swift:508:7:508:7 | SSA def(self) | -| test.swift:509:9:509:9 | self | test.swift:509:9:509:9 | SSA def(self) | -| test.swift:509:9:509:9 | self | test.swift:509:9:509:9 | SSA def(self) | -| test.swift:509:9:509:9 | self | test.swift:509:9:509:9 | SSA def(self) | -| test.swift:509:9:509:9 | value | test.swift:509:9:509:9 | SSA def(value) | -| test.swift:512:33:512:39 | SSA def(y) | test.swift:517:12:517:12 | y | -| test.swift:512:33:512:39 | y | test.swift:512:33:512:39 | SSA def(y) | -| test.swift:513:9:513:9 | SSA def(x) | test.swift:515:12:515:12 | x | -| test.swift:513:9:513:9 | x | test.swift:513:9:513:9 | SSA def(x) | -| test.swift:513:13:513:28 | call to optionalSource() | test.swift:513:9:513:9 | x | -| test.swift:514:9:514:9 | SSA def(cx) | test.swift:515:5:515:5 | cx | -| test.swift:514:9:514:9 | cx | test.swift:514:9:514:9 | SSA def(cx) | -| test.swift:514:14:514:16 | call to C.init() | test.swift:514:9:514:9 | cx | -| test.swift:515:5:515:5 | [post] cx | test.swift:519:20:519:20 | cx | -| test.swift:515:5:515:5 | cx | test.swift:519:20:519:20 | cx | -| test.swift:516:9:516:9 | SSA def(cy) | test.swift:517:5:517:5 | cy | -| test.swift:516:9:516:9 | cy | test.swift:516:9:516:9 | SSA def(cy) | -| test.swift:516:14:516:16 | call to C.init() | test.swift:516:9:516:9 | cy | -| test.swift:517:5:517:5 | [post] cy | test.swift:521:20:521:20 | cy | -| test.swift:517:5:517:5 | cy | test.swift:521:20:521:20 | cy | -| test.swift:519:15:519:15 | SSA def(z1) | test.swift:520:15:520:15 | z1 | -| test.swift:519:15:519:15 | z1 | test.swift:519:15:519:15 | SSA def(z1) | -| test.swift:519:20:519:23 | .x | test.swift:519:11:519:15 | let ...? | -| test.swift:521:15:521:15 | SSA def(z2) | test.swift:522:15:522:15 | z2 | -| test.swift:521:15:521:15 | z2 | test.swift:521:15:521:15 | SSA def(z2) | -| test.swift:521:20:521:23 | .x | test.swift:521:11:521:15 | let ...? | -| test.swift:526:14:526:21 | call to source() | test.swift:526:13:526:21 | call to +(_:) | -| test.swift:534:7:534:7 | SSA def(self) | test.swift:534:7:534:7 | self[return] | -| test.swift:534:7:534:7 | self | test.swift:534:7:534:7 | SSA def(self) | -| test.swift:535:9:535:9 | self | test.swift:535:9:535:9 | SSA def(self) | -| test.swift:535:9:535:9 | self | test.swift:535:9:535:9 | SSA def(self) | -| test.swift:535:9:535:9 | self | test.swift:535:9:535:9 | SSA def(self) | -| test.swift:535:9:535:9 | value | test.swift:535:9:535:9 | SSA def(value) | -| test.swift:536:5:536:5 | SSA def(self) | test.swift:537:7:537:7 | self | -| test.swift:536:5:536:5 | self | test.swift:536:5:536:5 | SSA def(self) | -| test.swift:536:10:536:13 | SSA def(s) | test.swift:537:13:537:13 | s | -| test.swift:536:10:536:13 | s | test.swift:536:10:536:13 | SSA def(s) | -| test.swift:537:7:537:7 | [post] self | test.swift:536:5:538:5 | self[return] | -| test.swift:537:7:537:7 | self | test.swift:536:5:538:5 | self[return] | -| test.swift:542:17:542:17 | SSA def(self) | test.swift:543:7:543:7 | self | -| test.swift:542:17:542:17 | self | test.swift:542:17:542:17 | SSA def(self) | -| test.swift:543:7:543:7 | [post] self | test.swift:544:17:544:17 | self | -| test.swift:543:7:543:7 | self | test.swift:544:17:544:17 | self | -| test.swift:544:17:544:17 | [post] self | test.swift:542:17:545:5 | self[return] | -| test.swift:544:17:544:17 | self | test.swift:542:17:545:5 | self[return] | -| test.swift:548:21:548:27 | SSA def(path) | test.swift:550:37:550:37 | path | -| test.swift:548:21:548:27 | path | test.swift:548:21:548:27 | SSA def(path) | -| test.swift:553:7:553:7 | SSA def(self) | test.swift:553:7:553:7 | self[return] | -| test.swift:553:7:553:7 | self | test.swift:553:7:553:7 | SSA def(self) | -| test.swift:554:3:554:3 | SSA def(self) | test.swift:554:3:554:40 | self[return] | -| test.swift:554:3:554:3 | self | test.swift:554:3:554:3 | SSA def(self) | -| test.swift:554:27:554:38 | SSA def(n) | test.swift:554:3:554:40 | n[return] | -| test.swift:554:31:554:38 | call to source() | test.swift:554:27:554:38 | SSA def(n) | -| test.swift:560:7:560:7 | SSA def(n) | test.swift:561:36:561:36 | n | -| test.swift:560:7:560:7 | n | test.swift:560:7:560:7 | SSA def(n) | -| test.swift:560:11:560:11 | 0 | test.swift:560:7:560:7 | n | -| test.swift:561:36:561:36 | n | test.swift:561:35:561:36 | &... | +| test.swift:375:16:375:21 | SSA def(v) | test.swift:375:61:375:61 | v | +| test.swift:375:16:375:21 | v | test.swift:375:16:375:21 | SSA def(v) | +| test.swift:377:18:377:23 | SSA def(v) | test.swift:377:59:377:59 | v | +| test.swift:377:18:377:23 | v | test.swift:377:18:377:23 | SSA def(v) | +| test.swift:381:9:381:9 | SSA def(a) | test.swift:383:12:383:12 | a | +| test.swift:381:9:381:9 | a | test.swift:381:9:381:9 | SSA def(a) | +| test.swift:381:9:381:13 | ... as ... | test.swift:381:9:381:9 | a | +| test.swift:381:22:381:23 | .myNone | test.swift:381:9:381:13 | ... as ... | +| test.swift:383:12:383:12 | a | test.swift:384:10:384:11 | .myNone | +| test.swift:383:12:383:12 | a | test.swift:386:10:386:25 | .mySingle(...) | +| test.swift:383:12:383:12 | a | test.swift:388:10:388:30 | .myPair(...) | +| test.swift:383:12:383:12 | a | test.swift:391:14:391:26 | .myCons(...) | +| test.swift:383:12:383:12 | a | test.swift:395:32:395:32 | a | +| test.swift:386:24:386:24 | SSA def(a) | test.swift:387:19:387:19 | a | +| test.swift:386:24:386:24 | a | test.swift:386:24:386:24 | SSA def(a) | +| test.swift:388:22:388:22 | SSA def(a) | test.swift:389:19:389:19 | a | +| test.swift:388:22:388:22 | a | test.swift:388:22:388:22 | SSA def(a) | +| test.swift:388:29:388:29 | SSA def(b) | test.swift:390:19:390:19 | b | +| test.swift:388:29:388:29 | b | test.swift:388:29:388:29 | SSA def(b) | +| test.swift:391:10:391:26 | SSA phi(a) | test.swift:392:19:392:19 | a | +| test.swift:391:22:391:22 | SSA def(a) | test.swift:391:10:391:26 | SSA phi(a) | +| test.swift:391:22:391:22 | a | test.swift:391:22:391:22 | SSA def(a) | +| test.swift:395:27:395:27 | SSA def(x) | test.swift:396:19:396:19 | x | +| test.swift:395:27:395:27 | x | test.swift:395:27:395:27 | SSA def(x) | +| test.swift:395:32:395:32 | a | test.swift:395:13:395:28 | .mySingle(...) | +| test.swift:395:32:395:32 | a | test.swift:398:37:398:37 | a | +| test.swift:398:25:398:25 | SSA def(x) | test.swift:399:19:399:19 | x | +| test.swift:398:25:398:25 | x | test.swift:398:25:398:25 | SSA def(x) | +| test.swift:398:32:398:32 | SSA def(y) | test.swift:400:19:400:19 | y | +| test.swift:398:32:398:32 | y | test.swift:398:32:398:32 | SSA def(y) | +| test.swift:398:37:398:37 | a | test.swift:398:13:398:33 | .myPair(...) | +| test.swift:403:5:403:27 | SSA def(a) | test.swift:405:12:405:12 | a | +| test.swift:403:9:403:27 | call to ... | test.swift:403:5:403:27 | SSA def(a) | +| test.swift:405:12:405:12 | a | test.swift:406:10:406:11 | .myNone | +| test.swift:405:12:405:12 | a | test.swift:408:10:408:25 | .mySingle(...) | +| test.swift:405:12:405:12 | a | test.swift:410:10:410:30 | .myPair(...) | +| test.swift:405:12:405:12 | a | test.swift:413:14:413:26 | .myCons(...) | +| test.swift:405:12:405:12 | a | test.swift:417:32:417:32 | a | +| test.swift:408:24:408:24 | SSA def(a) | test.swift:409:19:409:19 | a | +| test.swift:408:24:408:24 | a | test.swift:408:24:408:24 | SSA def(a) | +| test.swift:410:22:410:22 | SSA def(a) | test.swift:411:19:411:19 | a | +| test.swift:410:22:410:22 | a | test.swift:410:22:410:22 | SSA def(a) | +| test.swift:410:29:410:29 | SSA def(b) | test.swift:412:19:412:19 | b | +| test.swift:410:29:410:29 | b | test.swift:410:29:410:29 | SSA def(b) | +| test.swift:413:10:413:26 | SSA phi(a) | test.swift:414:19:414:19 | a | +| test.swift:413:22:413:22 | SSA def(a) | test.swift:413:10:413:26 | SSA phi(a) | +| test.swift:413:22:413:22 | a | test.swift:413:22:413:22 | SSA def(a) | +| test.swift:417:27:417:27 | SSA def(x) | test.swift:418:19:418:19 | x | +| test.swift:417:27:417:27 | x | test.swift:417:27:417:27 | SSA def(x) | +| test.swift:417:32:417:32 | a | test.swift:417:13:417:28 | .mySingle(...) | +| test.swift:417:32:417:32 | a | test.swift:420:37:420:37 | a | +| test.swift:420:25:420:25 | SSA def(x) | test.swift:421:19:421:19 | x | +| test.swift:420:25:420:25 | x | test.swift:420:25:420:25 | SSA def(x) | +| test.swift:420:32:420:32 | SSA def(y) | test.swift:422:19:422:19 | y | +| test.swift:420:32:420:32 | y | test.swift:420:32:420:32 | SSA def(y) | +| test.swift:420:37:420:37 | a | test.swift:420:13:420:33 | .myPair(...) | +| test.swift:425:5:425:34 | SSA def(a) | test.swift:427:12:427:12 | a | +| test.swift:425:9:425:34 | call to ... | test.swift:425:5:425:34 | SSA def(a) | +| test.swift:427:12:427:12 | a | test.swift:428:10:428:11 | .myNone | +| test.swift:427:12:427:12 | a | test.swift:430:10:430:25 | .mySingle(...) | +| test.swift:427:12:427:12 | a | test.swift:432:10:432:30 | .myPair(...) | +| test.swift:427:12:427:12 | a | test.swift:435:14:435:26 | .myCons(...) | +| test.swift:427:12:427:12 | a | test.swift:439:32:439:32 | a | +| test.swift:430:24:430:24 | SSA def(a) | test.swift:431:19:431:19 | a | +| test.swift:430:24:430:24 | a | test.swift:430:24:430:24 | SSA def(a) | +| test.swift:432:22:432:22 | SSA def(a) | test.swift:433:19:433:19 | a | +| test.swift:432:22:432:22 | a | test.swift:432:22:432:22 | SSA def(a) | +| test.swift:432:29:432:29 | SSA def(b) | test.swift:434:19:434:19 | b | +| test.swift:432:29:432:29 | b | test.swift:432:29:432:29 | SSA def(b) | +| test.swift:435:10:435:26 | SSA phi(a) | test.swift:436:19:436:19 | a | +| test.swift:435:22:435:22 | SSA def(a) | test.swift:435:10:435:26 | SSA phi(a) | +| test.swift:435:22:435:22 | a | test.swift:435:22:435:22 | SSA def(a) | +| test.swift:439:27:439:27 | SSA def(x) | test.swift:440:19:440:19 | x | +| test.swift:439:27:439:27 | x | test.swift:439:27:439:27 | SSA def(x) | +| test.swift:439:32:439:32 | a | test.swift:439:13:439:28 | .mySingle(...) | +| test.swift:439:32:439:32 | a | test.swift:442:37:442:37 | a | +| test.swift:442:25:442:25 | SSA def(x) | test.swift:443:19:443:19 | x | +| test.swift:442:25:442:25 | x | test.swift:442:25:442:25 | SSA def(x) | +| test.swift:442:32:442:32 | SSA def(y) | test.swift:444:19:444:19 | y | +| test.swift:442:32:442:32 | y | test.swift:442:32:442:32 | SSA def(y) | +| test.swift:442:37:442:37 | a | test.swift:442:13:442:33 | .myPair(...) | +| test.swift:442:37:442:37 | a | test.swift:447:33:447:33 | a | +| test.swift:447:9:447:9 | SSA def(b) | test.swift:449:12:449:12 | b | +| test.swift:447:9:447:9 | b | test.swift:447:9:447:9 | SSA def(b) | +| test.swift:447:9:447:12 | ... as ... | test.swift:447:9:447:9 | b | +| test.swift:447:21:447:34 | call to ... | test.swift:447:9:447:12 | ... as ... | +| test.swift:447:33:447:33 | [post] a | test.swift:476:13:476:13 | a | +| test.swift:447:33:447:33 | a | test.swift:476:13:476:13 | a | +| test.swift:449:12:449:12 | b | test.swift:450:10:450:11 | .myNone | +| test.swift:449:12:449:12 | b | test.swift:452:10:452:25 | .mySingle(...) | +| test.swift:449:12:449:12 | b | test.swift:454:10:454:30 | .myPair(...) | +| test.swift:449:12:449:12 | b | test.swift:457:14:457:38 | .myCons(...) | +| test.swift:449:12:449:12 | b | test.swift:461:14:461:26 | .myCons(...) | +| test.swift:449:12:449:12 | b | test.swift:472:45:472:45 | b | +| test.swift:452:24:452:24 | SSA def(a) | test.swift:453:19:453:19 | a | +| test.swift:452:24:452:24 | a | test.swift:452:24:452:24 | SSA def(a) | +| test.swift:454:22:454:22 | SSA def(a) | test.swift:455:19:455:19 | a | +| test.swift:454:22:454:22 | a | test.swift:454:22:454:22 | SSA def(a) | +| test.swift:454:29:454:29 | SSA def(b) | test.swift:456:19:456:19 | b | +| test.swift:454:29:454:29 | b | test.swift:454:29:454:29 | SSA def(b) | +| test.swift:457:10:457:38 | SSA phi(a) | test.swift:458:19:458:19 | a | +| test.swift:457:10:457:38 | SSA phi(b) | test.swift:459:19:459:19 | b | +| test.swift:457:10:457:38 | SSA phi(c) | test.swift:460:19:460:19 | c | +| test.swift:457:22:457:22 | SSA def(a) | test.swift:457:10:457:38 | SSA phi(a) | +| test.swift:457:22:457:22 | a | test.swift:457:22:457:22 | SSA def(a) | +| test.swift:457:33:457:33 | SSA def(b) | test.swift:457:10:457:38 | SSA phi(b) | +| test.swift:457:33:457:33 | b | test.swift:457:33:457:33 | SSA def(b) | +| test.swift:457:36:457:36 | SSA def(c) | test.swift:457:10:457:38 | SSA phi(c) | +| test.swift:457:36:457:36 | c | test.swift:457:36:457:36 | SSA def(c) | +| test.swift:461:10:461:26 | SSA phi(a) | test.swift:462:19:462:19 | a | +| test.swift:461:22:461:22 | SSA def(a) | test.swift:461:10:461:26 | SSA phi(a) | +| test.swift:461:22:461:22 | a | test.swift:461:22:461:22 | SSA def(a) | +| test.swift:465:27:465:27 | SSA def(x) | test.swift:466:19:466:19 | x | +| test.swift:465:27:465:27 | x | test.swift:465:27:465:27 | SSA def(x) | +| test.swift:465:32:465:57 | call to ... | test.swift:465:13:465:28 | .mySingle(...) | +| test.swift:468:31:468:31 | SSA def(x) | test.swift:469:19:469:19 | x | +| test.swift:468:31:468:31 | x | test.swift:468:31:468:31 | SSA def(x) | +| test.swift:468:38:468:38 | SSA def(y) | test.swift:470:19:470:19 | y | +| test.swift:468:38:468:38 | y | test.swift:468:38:468:38 | SSA def(y) | +| test.swift:468:43:468:62 | call to ... | test.swift:468:13:468:39 | .myPair(...) | +| test.swift:472:13:472:41 | SSA phi(c) | test.swift:473:19:473:19 | c | +| test.swift:472:39:472:39 | SSA def(c) | test.swift:472:13:472:41 | SSA phi(c) | +| test.swift:472:39:472:39 | c | test.swift:472:39:472:39 | SSA def(c) | +| test.swift:472:45:472:45 | b | test.swift:472:17:472:41 | .myCons(...) | +| test.swift:472:45:472:45 | b | test.swift:476:16:476:16 | b | +| test.swift:476:12:476:17 | (...) | test.swift:477:14:477:55 | (...) | +| test.swift:476:12:476:17 | (...) | test.swift:483:5:483:5 | _ | +| test.swift:477:10:477:55 | SSA phi(a) | test.swift:478:19:478:19 | a | +| test.swift:477:10:477:55 | SSA phi(b) | test.swift:479:19:479:19 | b | +| test.swift:477:10:477:55 | SSA phi(c) | test.swift:480:19:480:19 | c | +| test.swift:477:10:477:55 | SSA phi(d) | test.swift:481:19:481:19 | d | +| test.swift:477:10:477:55 | SSA phi(e) | test.swift:482:19:482:19 | e | +| test.swift:477:23:477:23 | SSA def(a) | test.swift:477:10:477:55 | SSA phi(a) | +| test.swift:477:23:477:23 | a | test.swift:477:23:477:23 | SSA def(a) | +| test.swift:477:26:477:26 | SSA def(b) | test.swift:477:10:477:55 | SSA phi(b) | +| test.swift:477:26:477:26 | b | test.swift:477:26:477:26 | SSA def(b) | +| test.swift:477:38:477:38 | SSA def(c) | test.swift:477:10:477:55 | SSA phi(c) | +| test.swift:477:38:477:38 | c | test.swift:477:38:477:38 | SSA def(c) | +| test.swift:477:49:477:49 | SSA def(d) | test.swift:477:10:477:55 | SSA phi(d) | +| test.swift:477:49:477:49 | d | test.swift:477:49:477:49 | SSA def(d) | +| test.swift:477:52:477:52 | SSA def(e) | test.swift:477:10:477:55 | SSA phi(e) | +| test.swift:477:52:477:52 | e | test.swift:477:52:477:52 | SSA def(e) | +| test.swift:487:9:487:9 | SSA def(c1) | test.swift:493:39:493:39 | c1 | +| test.swift:487:9:487:9 | c1 | test.swift:487:9:487:9 | SSA def(c1) | +| test.swift:487:14:487:31 | call to ... | test.swift:487:9:487:9 | c1 | +| test.swift:488:9:488:9 | SSA def(c2) | test.swift:494:39:494:39 | c2 | +| test.swift:488:9:488:9 | c2 | test.swift:488:9:488:9 | SSA def(c2) | +| test.swift:488:14:488:38 | call to ... | test.swift:488:9:488:9 | c2 | +| test.swift:489:9:489:9 | SSA def(c3) | test.swift:495:39:495:39 | c3 | +| test.swift:489:9:489:9 | c3 | test.swift:489:9:489:9 | SSA def(c3) | +| test.swift:489:14:489:25 | call to mkMyEnum1(_:) | test.swift:489:9:489:9 | c3 | +| test.swift:490:9:490:9 | SSA def(c4) | test.swift:496:39:496:39 | c4 | +| test.swift:490:9:490:9 | c4 | test.swift:490:9:490:9 | SSA def(c4) | +| test.swift:490:14:490:32 | call to mkMyEnum1(_:) | test.swift:490:9:490:9 | c4 | +| test.swift:491:9:491:9 | SSA def(c5) | test.swift:497:39:497:39 | c5 | +| test.swift:491:9:491:9 | c5 | test.swift:491:9:491:9 | SSA def(c5) | +| test.swift:491:14:491:25 | call to mkMyEnum2(_:) | test.swift:491:9:491:9 | c5 | +| test.swift:492:9:492:9 | SSA def(c6) | test.swift:498:39:498:39 | c6 | +| test.swift:492:9:492:9 | c6 | test.swift:492:9:492:9 | SSA def(c6) | +| test.swift:492:14:492:32 | call to mkMyEnum2(_:) | test.swift:492:9:492:9 | c6 | +| test.swift:493:33:493:33 | SSA def(d1) | test.swift:493:54:493:54 | d1 | +| test.swift:493:33:493:33 | d1 | test.swift:493:33:493:33 | SSA def(d1) | +| test.swift:493:39:493:39 | c1 | test.swift:493:13:493:35 | .mySingle(...) | +| test.swift:494:33:494:33 | SSA def(d2) | test.swift:494:54:494:54 | d2 | +| test.swift:494:33:494:33 | d2 | test.swift:494:33:494:33 | SSA def(d2) | +| test.swift:494:39:494:39 | c2 | test.swift:494:13:494:35 | .mySingle(...) | +| test.swift:495:33:495:33 | SSA def(d3) | test.swift:495:54:495:54 | d3 | +| test.swift:495:33:495:33 | d3 | test.swift:495:33:495:33 | SSA def(d3) | +| test.swift:495:39:495:39 | c3 | test.swift:495:13:495:35 | .mySingle(...) | +| test.swift:496:33:496:33 | SSA def(d4) | test.swift:496:54:496:54 | d4 | +| test.swift:496:33:496:33 | d4 | test.swift:496:33:496:33 | SSA def(d4) | +| test.swift:496:39:496:39 | c4 | test.swift:496:13:496:35 | .mySingle(...) | +| test.swift:497:33:497:33 | SSA def(d5) | test.swift:497:54:497:54 | d5 | +| test.swift:497:33:497:33 | d5 | test.swift:497:33:497:33 | SSA def(d5) | +| test.swift:497:39:497:39 | c5 | test.swift:497:13:497:35 | .mySingle(...) | +| test.swift:498:33:498:33 | SSA def(d6) | test.swift:498:54:498:54 | d6 | +| test.swift:498:33:498:33 | d6 | test.swift:498:33:498:33 | SSA def(d6) | +| test.swift:498:39:498:39 | c6 | test.swift:498:13:498:35 | .mySingle(...) | +| test.swift:500:9:500:9 | SSA def(e1) | test.swift:506:15:506:15 | e1 | +| test.swift:500:9:500:9 | e1 | test.swift:500:9:500:9 | SSA def(e1) | +| test.swift:500:14:500:29 | call to ... | test.swift:500:9:500:9 | e1 | +| test.swift:501:9:501:9 | SSA def(e2) | test.swift:507:15:507:15 | e2 | +| test.swift:501:9:501:9 | e2 | test.swift:501:9:501:9 | SSA def(e2) | +| test.swift:501:14:501:36 | call to ... | test.swift:501:9:501:9 | e2 | +| test.swift:502:9:502:9 | SSA def(e3) | test.swift:508:15:508:15 | e3 | +| test.swift:502:9:502:9 | e3 | test.swift:502:9:502:9 | SSA def(e3) | +| test.swift:502:14:502:27 | call to mkOptional1(_:) | test.swift:502:9:502:9 | e3 | +| test.swift:503:9:503:9 | SSA def(e4) | test.swift:509:15:509:15 | e4 | +| test.swift:503:9:503:9 | e4 | test.swift:503:9:503:9 | SSA def(e4) | +| test.swift:503:14:503:34 | call to mkOptional1(_:) | test.swift:503:9:503:9 | e4 | +| test.swift:504:9:504:9 | SSA def(e5) | test.swift:510:15:510:15 | e5 | +| test.swift:504:9:504:9 | e5 | test.swift:504:9:504:9 | SSA def(e5) | +| test.swift:504:14:504:27 | call to mkOptional2(_:) | test.swift:504:9:504:9 | e5 | +| test.swift:505:9:505:9 | SSA def(e6) | test.swift:511:15:511:15 | e6 | +| test.swift:505:9:505:9 | e6 | test.swift:505:9:505:9 | SSA def(e6) | +| test.swift:505:14:505:34 | call to mkOptional2(_:) | test.swift:505:9:505:9 | e6 | +| test.swift:506:15:506:15 | e1 | test.swift:506:15:506:17 | ...! | +| test.swift:507:15:507:15 | e2 | test.swift:507:15:507:17 | ...! | +| test.swift:508:15:508:15 | e3 | test.swift:508:15:508:17 | ...! | +| test.swift:509:15:509:15 | e4 | test.swift:509:15:509:17 | ...! | +| test.swift:510:15:510:15 | e5 | test.swift:510:15:510:17 | ...! | +| test.swift:511:15:511:15 | e6 | test.swift:511:15:511:17 | ...! | +| test.swift:516:21:516:27 | SSA def(y) | test.swift:519:27:519:27 | y | +| test.swift:516:21:516:27 | SSA def(y) | test.swift:524:22:524:22 | y | +| test.swift:516:21:516:27 | y | test.swift:516:21:516:27 | SSA def(y) | +| test.swift:517:9:517:9 | SSA def(x) | test.swift:519:16:519:16 | x | +| test.swift:517:9:517:9 | x | test.swift:517:9:517:9 | SSA def(x) | +| test.swift:517:13:517:28 | call to optionalSource() | test.swift:517:9:517:9 | x | +| test.swift:519:12:519:12 | SSA def(a) | test.swift:519:27:519:27 | SSA phi(a) | +| test.swift:519:12:519:12 | a | test.swift:519:12:519:12 | SSA def(a) | +| test.swift:519:16:519:16 | x | test.swift:519:8:519:12 | let ...? | +| test.swift:519:16:519:16 | x | test.swift:524:19:524:19 | x | +| test.swift:519:23:519:23 | SSA def(b) | test.swift:521:19:521:19 | b | +| test.swift:519:23:519:23 | b | test.swift:519:23:519:23 | SSA def(b) | +| test.swift:519:27:519:27 | SSA phi(a) | test.swift:520:19:520:19 | a | +| test.swift:519:27:519:27 | y | test.swift:519:19:519:23 | let ...? | +| test.swift:519:27:519:27 | y | test.swift:524:22:524:22 | y | +| test.swift:524:9:524:9 | SSA def(tuple1) | test.swift:525:12:525:12 | tuple1 | +| test.swift:524:9:524:9 | tuple1 | test.swift:524:9:524:9 | SSA def(tuple1) | +| test.swift:524:18:524:23 | (...) | test.swift:524:9:524:9 | tuple1 | +| test.swift:525:12:525:12 | tuple1 | test.swift:526:10:526:37 | (...) | +| test.swift:525:12:525:12 | tuple1 | test.swift:529:5:529:5 | _ | +| test.swift:526:21:526:21 | SSA def(a) | test.swift:527:19:527:19 | a | +| test.swift:526:21:526:21 | a | test.swift:526:21:526:21 | SSA def(a) | +| test.swift:526:35:526:35 | SSA def(b) | test.swift:528:19:528:19 | b | +| test.swift:526:35:526:35 | b | test.swift:526:35:526:35 | SSA def(b) | +| test.swift:533:13:533:13 | SSA def(x) | test.swift:534:19:534:19 | x | +| test.swift:533:13:533:13 | x | test.swift:533:13:533:13 | SSA def(x) | +| test.swift:533:16:533:16 | SSA def(y) | test.swift:535:19:535:19 | y | +| test.swift:533:16:533:16 | y | test.swift:533:16:533:16 | SSA def(y) | +| test.swift:533:21:533:29 | call to source2() | test.swift:533:8:533:17 | let ...? | +| test.swift:539:7:539:7 | SSA def(self) | test.swift:539:7:539:7 | self[return] | +| test.swift:539:7:539:7 | SSA def(self) | test.swift:539:7:539:7 | self[return] | +| test.swift:539:7:539:7 | self | test.swift:539:7:539:7 | SSA def(self) | +| test.swift:539:7:539:7 | self | test.swift:539:7:539:7 | SSA def(self) | +| test.swift:540:9:540:9 | self | test.swift:540:9:540:9 | SSA def(self) | +| test.swift:540:9:540:9 | self | test.swift:540:9:540:9 | SSA def(self) | +| test.swift:540:9:540:9 | self | test.swift:540:9:540:9 | SSA def(self) | +| test.swift:540:9:540:9 | value | test.swift:540:9:540:9 | SSA def(value) | +| test.swift:543:33:543:39 | SSA def(y) | test.swift:548:12:548:12 | y | +| test.swift:543:33:543:39 | y | test.swift:543:33:543:39 | SSA def(y) | +| test.swift:544:9:544:9 | SSA def(x) | test.swift:546:12:546:12 | x | +| test.swift:544:9:544:9 | x | test.swift:544:9:544:9 | SSA def(x) | +| test.swift:544:13:544:28 | call to optionalSource() | test.swift:544:9:544:9 | x | +| test.swift:545:9:545:9 | SSA def(cx) | test.swift:546:5:546:5 | cx | +| test.swift:545:9:545:9 | cx | test.swift:545:9:545:9 | SSA def(cx) | +| test.swift:545:14:545:16 | call to C.init() | test.swift:545:9:545:9 | cx | +| test.swift:546:5:546:5 | [post] cx | test.swift:550:20:550:20 | cx | +| test.swift:546:5:546:5 | cx | test.swift:550:20:550:20 | cx | +| test.swift:547:9:547:9 | SSA def(cy) | test.swift:548:5:548:5 | cy | +| test.swift:547:9:547:9 | cy | test.swift:547:9:547:9 | SSA def(cy) | +| test.swift:547:14:547:16 | call to C.init() | test.swift:547:9:547:9 | cy | +| test.swift:548:5:548:5 | [post] cy | test.swift:552:20:552:20 | cy | +| test.swift:548:5:548:5 | cy | test.swift:552:20:552:20 | cy | +| test.swift:550:15:550:15 | SSA def(z1) | test.swift:551:15:551:15 | z1 | +| test.swift:550:15:550:15 | z1 | test.swift:550:15:550:15 | SSA def(z1) | +| test.swift:550:20:550:23 | .x | test.swift:550:11:550:15 | let ...? | +| test.swift:552:15:552:15 | SSA def(z2) | test.swift:553:15:553:15 | z2 | +| test.swift:552:15:552:15 | z2 | test.swift:552:15:552:15 | SSA def(z2) | +| test.swift:552:20:552:23 | .x | test.swift:552:11:552:15 | let ...? | +| test.swift:557:14:557:21 | call to source() | test.swift:557:13:557:21 | call to +(_:) | +| test.swift:565:7:565:7 | SSA def(self) | test.swift:565:7:565:7 | self[return] | | test.swift:565:7:565:7 | self | test.swift:565:7:565:7 | SSA def(self) | -| test.swift:567:3:567:3 | SSA def(self) | test.swift:568:5:568:5 | self | -| test.swift:567:3:567:3 | self | test.swift:567:3:567:3 | SSA def(self) | -| test.swift:567:8:567:11 | SSA def(x) | test.swift:568:14:568:14 | x | -| test.swift:567:8:567:11 | x | test.swift:567:8:567:11 | SSA def(x) | -| test.swift:568:5:568:5 | [post] self | test.swift:567:3:569:3 | self[return] | -| test.swift:568:5:568:5 | self | test.swift:567:3:569:3 | self[return] | -| test.swift:573:7:573:7 | SSA def(s) | test.swift:575:13:575:13 | s | -| test.swift:573:7:573:7 | s | test.swift:573:7:573:7 | SSA def(s) | -| test.swift:573:11:573:24 | call to S.init(x:) | test.swift:573:7:573:7 | s | -| test.swift:574:7:574:7 | SSA def(f) | test.swift:575:24:575:24 | f | -| test.swift:574:7:574:7 | f | test.swift:574:7:574:7 | SSA def(f) | -| test.swift:574:11:574:14 | #keyPath(...) | test.swift:574:7:574:7 | f | -| test.swift:574:11:574:14 | enter #keyPath(...) | test.swift:574:14:574:14 | KeyPathComponent | -| test.swift:575:13:575:13 | s | test.swift:578:13:578:13 | s | -| test.swift:577:7:577:7 | SSA def(inferred) | test.swift:578:24:578:24 | inferred | -| test.swift:577:7:577:7 | inferred | test.swift:577:7:577:7 | SSA def(inferred) | -| test.swift:577:7:577:32 | ... as ... | test.swift:577:7:577:7 | inferred | -| test.swift:577:36:577:38 | #keyPath(...) | test.swift:577:7:577:32 | ... as ... | -| test.swift:577:36:577:38 | enter #keyPath(...) | test.swift:577:38:577:38 | KeyPathComponent | -| test.swift:582:7:582:7 | self | test.swift:582:7:582:7 | SSA def(self) | -| test.swift:584:3:584:3 | SSA def(self) | test.swift:585:5:585:5 | self | -| test.swift:584:3:584:3 | self | test.swift:584:3:584:3 | SSA def(self) | -| test.swift:584:8:584:11 | SSA def(s) | test.swift:585:14:585:14 | s | -| test.swift:584:8:584:11 | s | test.swift:584:8:584:11 | SSA def(s) | -| test.swift:585:5:585:5 | [post] self | test.swift:584:3:586:3 | self[return] | -| test.swift:585:5:585:5 | self | test.swift:584:3:586:3 | self[return] | -| test.swift:590:7:590:7 | SSA def(s) | test.swift:591:18:591:18 | s | -| test.swift:590:7:590:7 | s | test.swift:590:7:590:7 | SSA def(s) | -| test.swift:590:11:590:24 | call to S.init(x:) | test.swift:590:7:590:7 | s | -| test.swift:591:7:591:7 | SSA def(s2) | test.swift:593:13:593:13 | s2 | -| test.swift:591:7:591:7 | s2 | test.swift:591:7:591:7 | SSA def(s2) | -| test.swift:591:12:591:19 | call to S2.init(s:) | test.swift:591:7:591:7 | s2 | -| test.swift:592:7:592:7 | SSA def(f) | test.swift:593:25:593:25 | f | -| test.swift:592:7:592:7 | f | test.swift:592:7:592:7 | SSA def(f) | -| test.swift:592:11:592:17 | #keyPath(...) | test.swift:592:7:592:7 | f | -| test.swift:592:11:592:17 | enter #keyPath(...) | test.swift:592:15:592:15 | KeyPathComponent | -| test.swift:597:9:597:9 | SSA def(array) | test.swift:599:15:599:15 | array | -| test.swift:597:9:597:9 | array | test.swift:597:9:597:9 | SSA def(array) | -| test.swift:597:17:597:26 | [...] | test.swift:597:9:597:9 | array | -| test.swift:598:9:598:9 | SSA def(f) | test.swift:599:30:599:30 | f | -| test.swift:598:9:598:9 | f | test.swift:598:9:598:9 | SSA def(f) | -| test.swift:598:13:598:22 | #keyPath(...) | test.swift:598:9:598:9 | f | -| test.swift:598:13:598:22 | enter #keyPath(...) | test.swift:598:20:598:22 | KeyPathComponent | -| test.swift:603:7:603:7 | self | test.swift:603:7:603:7 | SSA def(self) | -| test.swift:605:3:605:3 | SSA def(self) | test.swift:606:5:606:5 | self | -| test.swift:605:3:605:3 | self | test.swift:605:3:605:3 | SSA def(self) | -| test.swift:605:8:605:12 | SSA def(s) | test.swift:606:14:606:14 | s | -| test.swift:605:8:605:12 | s | test.swift:605:8:605:12 | SSA def(s) | -| test.swift:606:5:606:5 | [post] self | test.swift:605:3:607:3 | self[return] | -| test.swift:606:5:606:5 | self | test.swift:605:3:607:3 | self[return] | -| test.swift:611:9:611:9 | SSA def(s) | test.swift:612:29:612:29 | s | -| test.swift:611:9:611:9 | s | test.swift:611:9:611:9 | SSA def(s) | -| test.swift:611:13:611:26 | call to S.init(x:) | test.swift:611:9:611:9 | s | -| test.swift:612:9:612:9 | SSA def(s2) | test.swift:614:15:614:15 | s2 | -| test.swift:612:9:612:9 | s2 | test.swift:612:9:612:9 | SSA def(s2) | -| test.swift:612:14:612:30 | call to S2_Optional.init(s:) | test.swift:612:9:612:9 | s2 | -| test.swift:613:9:613:9 | SSA def(f) | test.swift:614:27:614:27 | f | -| test.swift:613:9:613:9 | f | test.swift:613:9:613:9 | SSA def(f) | -| test.swift:613:13:613:29 | #keyPath(...) | test.swift:613:9:613:9 | f | -| test.swift:613:13:613:29 | enter #keyPath(...) | test.swift:613:26:613:26 | KeyPathComponent | -| test.swift:618:9:618:9 | SSA def(x) | test.swift:622:9:622:9 | x | -| test.swift:618:9:618:9 | x | test.swift:618:9:618:9 | SSA def(x) | -| test.swift:618:13:618:20 | call to source() | test.swift:618:9:618:9 | x | -| test.swift:619:9:619:9 | SSA def(y) | test.swift:623:9:623:9 | y | -| test.swift:619:9:619:9 | y | test.swift:619:9:619:9 | SSA def(y) | -| test.swift:619:13:619:13 | 0 | test.swift:619:9:619:9 | y | -| test.swift:620:9:620:12 | ... as ... | test.swift:620:9:620:9 | t | -| test.swift:622:5:622:9 | SSA def(t) | test.swift:624:9:624:9 | t | -| test.swift:622:9:622:9 | x | test.swift:622:5:622:9 | SSA def(t) | -| test.swift:623:5:623:9 | SSA def(x) | test.swift:625:15:625:15 | x | -| test.swift:623:9:623:9 | y | test.swift:623:5:623:9 | SSA def(x) | -| test.swift:624:5:624:9 | SSA def(y) | test.swift:626:15:626:15 | y | -| test.swift:624:9:624:9 | t | test.swift:624:5:624:9 | SSA def(y) | -| test.swift:628:5:628:16 | SSA def(x) | test.swift:630:11:630:11 | x | -| test.swift:628:9:628:16 | call to source() | test.swift:628:5:628:16 | SSA def(x) | -| test.swift:629:5:629:9 | SSA def(y) | test.swift:630:15:630:15 | y | -| test.swift:629:9:629:9 | 0 | test.swift:629:5:629:9 | SSA def(y) | -| test.swift:630:10:630:11 | &... | test.swift:631:15:631:15 | x | -| test.swift:630:10:630:11 | [post] &... | test.swift:631:15:631:15 | x | -| test.swift:630:11:630:11 | x | test.swift:630:10:630:11 | &... | -| test.swift:630:14:630:15 | &... | test.swift:632:15:632:15 | y | -| test.swift:630:14:630:15 | [post] &... | test.swift:632:15:632:15 | y | -| test.swift:630:15:630:15 | y | test.swift:630:14:630:15 | &... | +| test.swift:566:9:566:9 | self | test.swift:566:9:566:9 | SSA def(self) | +| test.swift:566:9:566:9 | self | test.swift:566:9:566:9 | SSA def(self) | +| test.swift:566:9:566:9 | self | test.swift:566:9:566:9 | SSA def(self) | +| test.swift:566:9:566:9 | value | test.swift:566:9:566:9 | SSA def(value) | +| test.swift:567:5:567:5 | SSA def(self) | test.swift:568:7:568:7 | self | +| test.swift:567:5:567:5 | self | test.swift:567:5:567:5 | SSA def(self) | +| test.swift:567:10:567:13 | SSA def(s) | test.swift:568:13:568:13 | s | +| test.swift:567:10:567:13 | s | test.swift:567:10:567:13 | SSA def(s) | +| test.swift:568:7:568:7 | [post] self | test.swift:567:5:569:5 | self[return] | +| test.swift:568:7:568:7 | self | test.swift:567:5:569:5 | self[return] | +| test.swift:573:17:573:17 | SSA def(self) | test.swift:574:7:574:7 | self | +| test.swift:573:17:573:17 | self | test.swift:573:17:573:17 | SSA def(self) | +| test.swift:574:7:574:7 | [post] self | test.swift:575:17:575:17 | self | +| test.swift:574:7:574:7 | self | test.swift:575:17:575:17 | self | +| test.swift:575:17:575:17 | [post] self | test.swift:573:17:576:5 | self[return] | +| test.swift:575:17:575:17 | self | test.swift:573:17:576:5 | self[return] | +| test.swift:579:21:579:27 | SSA def(path) | test.swift:581:37:581:37 | path | +| test.swift:579:21:579:27 | path | test.swift:579:21:579:27 | SSA def(path) | +| test.swift:584:7:584:7 | SSA def(self) | test.swift:584:7:584:7 | self[return] | +| test.swift:584:7:584:7 | self | test.swift:584:7:584:7 | SSA def(self) | +| test.swift:585:3:585:3 | SSA def(self) | test.swift:585:3:585:40 | self[return] | +| test.swift:585:3:585:3 | self | test.swift:585:3:585:3 | SSA def(self) | +| test.swift:585:27:585:38 | SSA def(n) | test.swift:585:3:585:40 | n[return] | +| test.swift:585:31:585:38 | call to source() | test.swift:585:27:585:38 | SSA def(n) | +| test.swift:591:7:591:7 | SSA def(n) | test.swift:592:36:592:36 | n | +| test.swift:591:7:591:7 | n | test.swift:591:7:591:7 | SSA def(n) | +| test.swift:591:11:591:11 | 0 | test.swift:591:7:591:7 | n | +| test.swift:592:36:592:36 | [post] n | test.swift:592:35:592:36 | &... | +| test.swift:592:36:592:36 | n | test.swift:592:35:592:36 | &... | +| test.swift:596:7:596:7 | self | test.swift:596:7:596:7 | SSA def(self) | +| test.swift:598:3:598:3 | SSA def(self) | test.swift:599:5:599:5 | self | +| test.swift:598:3:598:3 | self | test.swift:598:3:598:3 | SSA def(self) | +| test.swift:598:8:598:11 | SSA def(x) | test.swift:599:14:599:14 | x | +| test.swift:598:8:598:11 | x | test.swift:598:8:598:11 | SSA def(x) | +| test.swift:599:5:599:5 | [post] self | test.swift:598:3:600:3 | self[return] | +| test.swift:599:5:599:5 | self | test.swift:598:3:600:3 | self[return] | +| test.swift:604:7:604:7 | SSA def(s) | test.swift:606:13:606:13 | s | +| test.swift:604:7:604:7 | s | test.swift:604:7:604:7 | SSA def(s) | +| test.swift:604:11:604:24 | call to S.init(x:) | test.swift:604:7:604:7 | s | +| test.swift:605:7:605:7 | SSA def(f) | test.swift:606:24:606:24 | f | +| test.swift:605:7:605:7 | f | test.swift:605:7:605:7 | SSA def(f) | +| test.swift:605:11:605:14 | #keyPath(...) | test.swift:605:7:605:7 | f | +| test.swift:605:11:605:14 | enter #keyPath(...) | test.swift:605:14:605:14 | KeyPathComponent | +| test.swift:606:13:606:13 | s | test.swift:609:13:609:13 | s | +| test.swift:608:7:608:7 | SSA def(inferred) | test.swift:609:24:609:24 | inferred | +| test.swift:608:7:608:7 | inferred | test.swift:608:7:608:7 | SSA def(inferred) | +| test.swift:608:7:608:32 | ... as ... | test.swift:608:7:608:7 | inferred | +| test.swift:608:36:608:38 | #keyPath(...) | test.swift:608:7:608:32 | ... as ... | +| test.swift:608:36:608:38 | enter #keyPath(...) | test.swift:608:38:608:38 | KeyPathComponent | +| test.swift:613:7:613:7 | self | test.swift:613:7:613:7 | SSA def(self) | +| test.swift:615:3:615:3 | SSA def(self) | test.swift:616:5:616:5 | self | +| test.swift:615:3:615:3 | self | test.swift:615:3:615:3 | SSA def(self) | +| test.swift:615:8:615:11 | SSA def(s) | test.swift:616:14:616:14 | s | +| test.swift:615:8:615:11 | s | test.swift:615:8:615:11 | SSA def(s) | +| test.swift:616:5:616:5 | [post] self | test.swift:615:3:617:3 | self[return] | +| test.swift:616:5:616:5 | self | test.swift:615:3:617:3 | self[return] | +| test.swift:621:7:621:7 | SSA def(s) | test.swift:622:18:622:18 | s | +| test.swift:621:7:621:7 | s | test.swift:621:7:621:7 | SSA def(s) | +| test.swift:621:11:621:24 | call to S.init(x:) | test.swift:621:7:621:7 | s | +| test.swift:622:7:622:7 | SSA def(s2) | test.swift:624:13:624:13 | s2 | +| test.swift:622:7:622:7 | s2 | test.swift:622:7:622:7 | SSA def(s2) | +| test.swift:622:12:622:19 | call to S2.init(s:) | test.swift:622:7:622:7 | s2 | +| test.swift:623:7:623:7 | SSA def(f) | test.swift:624:25:624:25 | f | +| test.swift:623:7:623:7 | f | test.swift:623:7:623:7 | SSA def(f) | +| test.swift:623:11:623:17 | #keyPath(...) | test.swift:623:7:623:7 | f | +| test.swift:623:11:623:17 | enter #keyPath(...) | test.swift:623:15:623:15 | KeyPathComponent | +| test.swift:628:9:628:9 | SSA def(array) | test.swift:630:15:630:15 | array | +| test.swift:628:9:628:9 | array | test.swift:628:9:628:9 | SSA def(array) | +| test.swift:628:17:628:26 | [...] | test.swift:628:9:628:9 | array | +| test.swift:629:9:629:9 | SSA def(f) | test.swift:630:30:630:30 | f | +| test.swift:629:9:629:9 | f | test.swift:629:9:629:9 | SSA def(f) | +| test.swift:629:13:629:22 | #keyPath(...) | test.swift:629:9:629:9 | f | +| test.swift:629:13:629:22 | enter #keyPath(...) | test.swift:629:20:629:22 | KeyPathComponent | +| test.swift:634:7:634:7 | self | test.swift:634:7:634:7 | SSA def(self) | +| test.swift:636:3:636:3 | SSA def(self) | test.swift:637:5:637:5 | self | +| test.swift:636:3:636:3 | self | test.swift:636:3:636:3 | SSA def(self) | +| test.swift:636:8:636:12 | SSA def(s) | test.swift:637:14:637:14 | s | +| test.swift:636:8:636:12 | s | test.swift:636:8:636:12 | SSA def(s) | +| test.swift:637:5:637:5 | [post] self | test.swift:636:3:638:3 | self[return] | +| test.swift:637:5:637:5 | self | test.swift:636:3:638:3 | self[return] | +| test.swift:642:9:642:9 | SSA def(s) | test.swift:643:29:643:29 | s | +| test.swift:642:9:642:9 | s | test.swift:642:9:642:9 | SSA def(s) | +| test.swift:642:13:642:26 | call to S.init(x:) | test.swift:642:9:642:9 | s | +| test.swift:643:9:643:9 | SSA def(s2) | test.swift:645:15:645:15 | s2 | +| test.swift:643:9:643:9 | s2 | test.swift:643:9:643:9 | SSA def(s2) | +| test.swift:643:14:643:30 | call to S2_Optional.init(s:) | test.swift:643:9:643:9 | s2 | +| test.swift:644:9:644:9 | SSA def(f) | test.swift:645:27:645:27 | f | +| test.swift:644:9:644:9 | f | test.swift:644:9:644:9 | SSA def(f) | +| test.swift:644:13:644:29 | #keyPath(...) | test.swift:644:9:644:9 | f | +| test.swift:644:13:644:29 | enter #keyPath(...) | test.swift:644:26:644:26 | KeyPathComponent | +| test.swift:649:9:649:9 | SSA def(x) | test.swift:653:9:653:9 | x | +| test.swift:649:9:649:9 | x | test.swift:649:9:649:9 | SSA def(x) | +| test.swift:649:13:649:20 | call to source() | test.swift:649:9:649:9 | x | +| test.swift:650:9:650:9 | SSA def(y) | test.swift:654:9:654:9 | y | +| test.swift:650:9:650:9 | y | test.swift:650:9:650:9 | SSA def(y) | +| test.swift:650:13:650:13 | 0 | test.swift:650:9:650:9 | y | +| test.swift:651:9:651:12 | ... as ... | test.swift:651:9:651:9 | t | +| test.swift:653:5:653:9 | SSA def(t) | test.swift:655:9:655:9 | t | +| test.swift:653:9:653:9 | x | test.swift:653:5:653:9 | SSA def(t) | +| test.swift:654:5:654:9 | SSA def(x) | test.swift:656:15:656:15 | x | +| test.swift:654:9:654:9 | y | test.swift:654:5:654:9 | SSA def(x) | +| test.swift:655:5:655:9 | SSA def(y) | test.swift:657:15:657:15 | y | +| test.swift:655:9:655:9 | t | test.swift:655:5:655:9 | SSA def(y) | +| test.swift:659:5:659:16 | SSA def(x) | test.swift:661:11:661:11 | x | +| test.swift:659:9:659:16 | call to source() | test.swift:659:5:659:16 | SSA def(x) | +| test.swift:660:5:660:9 | SSA def(y) | test.swift:661:15:661:15 | y | +| test.swift:660:9:660:9 | 0 | test.swift:660:5:660:9 | SSA def(y) | +| test.swift:661:10:661:11 | &... | test.swift:662:15:662:15 | x | +| test.swift:661:11:661:11 | [post] x | test.swift:661:10:661:11 | &... | +| test.swift:661:11:661:11 | x | test.swift:661:10:661:11 | &... | +| test.swift:661:14:661:15 | &... | test.swift:663:15:663:15 | y | +| test.swift:661:15:661:15 | [post] y | test.swift:661:14:661:15 | &... | +| test.swift:661:15:661:15 | y | test.swift:661:14:661:15 | &... | +| test.swift:667:9:667:9 | SSA def(arr1) | test.swift:668:15:668:15 | arr1 | +| test.swift:667:9:667:9 | arr1 | test.swift:667:9:667:9 | SSA def(arr1) | +| test.swift:667:16:667:22 | [...] | test.swift:667:9:667:9 | arr1 | +| test.swift:668:15:668:15 | &... | test.swift:669:5:669:5 | arr1 | +| test.swift:668:15:668:15 | [post] arr1 | test.swift:668:15:668:15 | &... | +| test.swift:668:15:668:15 | arr1 | test.swift:668:15:668:15 | &... | +| test.swift:669:5:669:5 | &... | test.swift:670:15:670:15 | arr1 | +| test.swift:669:5:669:5 | [post] arr1 | test.swift:669:5:669:5 | &... | +| test.swift:669:5:669:5 | arr1 | test.swift:669:5:669:5 | &... | +| test.swift:670:15:670:15 | &... | test.swift:671:15:671:15 | arr1 | +| test.swift:670:15:670:15 | [post] arr1 | test.swift:670:15:670:15 | &... | +| test.swift:670:15:670:15 | arr1 | test.swift:670:15:670:15 | &... | +| test.swift:673:9:673:9 | SSA def(arr2) | test.swift:674:15:674:15 | arr2 | +| test.swift:673:9:673:9 | arr2 | test.swift:673:9:673:9 | SSA def(arr2) | +| test.swift:673:16:673:25 | [...] | test.swift:673:9:673:9 | arr2 | +| test.swift:674:15:674:15 | &... | test.swift:685:16:685:16 | arr2 | +| test.swift:674:15:674:15 | [post] arr2 | test.swift:674:15:674:15 | &... | +| test.swift:674:15:674:15 | arr2 | test.swift:674:15:674:15 | &... | +| test.swift:676:9:676:9 | SSA def(matrix) | test.swift:677:15:677:15 | matrix | +| test.swift:676:9:676:9 | matrix | test.swift:676:9:676:9 | SSA def(matrix) | +| test.swift:676:18:676:29 | [...] | test.swift:676:9:676:9 | matrix | +| test.swift:677:15:677:15 | &... | test.swift:678:15:678:15 | matrix | +| test.swift:677:15:677:15 | [post] matrix | test.swift:677:15:677:15 | &... | +| test.swift:677:15:677:15 | matrix | test.swift:677:15:677:15 | &... | +| test.swift:678:15:678:15 | [post] matrix | test.swift:678:15:678:15 | &... | +| test.swift:678:15:678:15 | matrix | test.swift:678:15:678:15 | &... | +| test.swift:678:15:678:23 | ...[...] | test.swift:678:15:678:23 | &... | +| test.swift:680:9:680:9 | SSA def(matrix2) | test.swift:681:5:681:5 | matrix2 | +| test.swift:680:9:680:9 | matrix2 | test.swift:680:9:680:9 | SSA def(matrix2) | +| test.swift:680:19:680:23 | [...] | test.swift:680:9:680:9 | matrix2 | +| test.swift:681:5:681:5 | &... | test.swift:682:15:682:15 | matrix2 | +| test.swift:681:5:681:5 | [post] matrix2 | test.swift:681:5:681:5 | &... | +| test.swift:681:5:681:5 | matrix2 | test.swift:681:5:681:5 | &... | +| test.swift:681:5:681:14 | ...[...] | test.swift:681:5:681:14 | &... | +| test.swift:682:15:682:15 | [post] matrix2 | test.swift:682:15:682:15 | &... | +| test.swift:682:15:682:15 | matrix2 | test.swift:682:15:682:15 | &... | +| test.swift:682:15:682:24 | ...[...] | test.swift:682:15:682:24 | &... | +| test.swift:684:9:684:9 | SSA def(arr3) | test.swift:685:23:685:23 | arr3 | +| test.swift:684:9:684:9 | arr3 | test.swift:684:9:684:9 | SSA def(arr3) | +| test.swift:684:16:684:18 | [...] | test.swift:684:9:684:9 | arr3 | +| test.swift:685:9:685:9 | SSA def(arr4) | test.swift:687:15:687:15 | arr4 | +| test.swift:685:9:685:9 | arr4 | test.swift:685:9:685:9 | SSA def(arr4) | +| test.swift:685:16:685:23 | ... .+(_:_:) ... | test.swift:685:9:685:9 | arr4 | +| test.swift:685:23:685:23 | arr3 | test.swift:686:15:686:15 | arr3 | +| test.swift:686:15:686:15 | [post] arr3 | test.swift:686:15:686:15 | &... | +| test.swift:686:15:686:15 | arr3 | test.swift:686:15:686:15 | &... | +| test.swift:687:15:687:15 | [post] arr4 | test.swift:687:15:687:15 | &... | +| test.swift:687:15:687:15 | arr4 | test.swift:687:15:687:15 | &... | +| test.swift:689:9:689:9 | SSA def(arr5) | test.swift:690:15:690:15 | arr5 | +| test.swift:689:9:689:9 | arr5 | test.swift:689:9:689:9 | SSA def(arr5) | +| test.swift:689:16:689:51 | call to Array.init(repeating:count:) | test.swift:689:9:689:9 | arr5 | +| test.swift:690:15:690:15 | [post] arr5 | test.swift:690:15:690:15 | &... | +| test.swift:690:15:690:15 | arr5 | test.swift:690:15:690:15 | &... | +| test.swift:692:9:692:9 | SSA def(arr6) | test.swift:693:5:693:5 | arr6 | +| test.swift:692:9:692:9 | arr6 | test.swift:692:9:692:9 | SSA def(arr6) | +| test.swift:692:16:692:22 | [...] | test.swift:692:9:692:9 | arr6 | +| test.swift:693:5:693:5 | &... | test.swift:694:15:694:15 | arr6 | +| test.swift:693:5:693:5 | [post] arr6 | test.swift:693:5:693:5 | &... | +| test.swift:693:5:693:5 | arr6 | test.swift:693:5:693:5 | &... | +| test.swift:694:15:694:15 | [post] arr6 | test.swift:694:15:694:15 | &... | +| test.swift:694:15:694:15 | arr6 | test.swift:694:15:694:15 | &... | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/test.swift b/swift/ql/test/library-tests/dataflow/dataflow/test.swift index 64af76c343f..e789964a54f 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/test.swift +++ b/swift/ql/test/library-tests/dataflow/dataflow/test.swift @@ -1,5 +1,5 @@ func source() -> Int { return 0; } -func sink(arg: Int) {} +func sink(arg: T) {} func intraprocedural_with_local_flow() -> Void { var t2: Int @@ -372,6 +372,11 @@ indirect enum MyEnum { case myCons(Int, MyEnum) } +func mkMyEnum1(_ v: Int) -> MyEnum { return MyEnum.mySingle(v) } +func mkMyEnum2(_ v: Int) -> MyEnum { return MyEnum.myNone } // modelled flow +func mkOptional1(_ v: Int) -> Int? { return Optional.some(v) } +func mkOptional2(_ v: Int) -> Int? { return nil } // modelled flow + func testEnums() { var a : MyEnum = .myNone @@ -401,7 +406,7 @@ func testEnums() { case .myNone: () case .mySingle(let a): - sink(arg: a) // $ flow=398 + sink(arg: a) // $ flow=403 case .myPair(let a, let b): sink(arg: a) sink(arg: b) @@ -410,7 +415,7 @@ func testEnums() { } if case .mySingle(let x) = a { - sink(arg: x) // $ flow=398 + sink(arg: x) // $ flow=403 } if case .myPair(let x, let y) = a { sink(arg: x) @@ -426,7 +431,7 @@ func testEnums() { sink(arg: a) case .myPair(let a, let b): sink(arg: a) - sink(arg: b) // $ flow=420 + sink(arg: b) // $ flow=425 case let .myCons(a, _): sink(arg: a) } @@ -436,7 +441,7 @@ func testEnums() { } if case .myPair(let x, let y) = a { sink(arg: x) - sink(arg: y) // $ flow=420 + sink(arg: y) // $ flow=425 } let b: MyEnum = .myCons(42, a) @@ -452,7 +457,7 @@ func testEnums() { case let .myCons(a, .myPair(b, c)): sink(arg: a) sink(arg: b) - sink(arg: c) // $ flow=420 + sink(arg: c) // $ flow=425 case let .myCons(a, _): sink(arg: a) } @@ -461,23 +466,49 @@ func testEnums() { sink(arg: x) } if case MyEnum.myPair(let x, let y) = .myPair(source(), 0) { - sink(arg: x) // $ flow=463 + sink(arg: x) // $ flow=468 sink(arg: y) } if case let .myCons(_, .myPair(_, c)) = b { - sink(arg: c) // $ flow=420 + sink(arg: c) // $ flow=425 } switch (a, b) { case let (.myPair(a, b), .myCons(c, .myPair(d, e))): sink(arg: a) - sink(arg: b) // $ flow=420 + sink(arg: b) // $ flow=425 sink(arg: c) sink(arg: d) - sink(arg: e) // $ flow=420 + sink(arg: e) // $ flow=425 default: () } + + let c1 = MyEnum.mySingle(0) + let c2 = MyEnum.mySingle(source()) + let c3 = mkMyEnum1(0) + let c4 = mkMyEnum1(source()) + let c5 = mkMyEnum2(0) + let c6 = mkMyEnum2(source()) + if case MyEnum.mySingle(let d1) = c1 { sink(arg: d1) } + if case MyEnum.mySingle(let d2) = c2 { sink(arg: d2) } // $ flow=488 + if case MyEnum.mySingle(let d3) = c3 { sink(arg: d3) } + if case MyEnum.mySingle(let d4) = c4 { sink(arg: d4) } // $ flow=490 + if case MyEnum.mySingle(let d5) = c5 { sink(arg: d5) } + if case MyEnum.mySingle(let d6) = c6 { sink(arg: d6) } // $ flow=492 + + let e1 = Optional.some(0) + let e2 = Optional.some(source()) + let e3 = mkOptional1(0) + let e4 = mkOptional1(source()) + let e5 = mkOptional2(0) + let e6 = mkOptional2(source()) + sink(arg: e1!) + sink(arg: e2!) // $ flow=501 + sink(arg: e3!) + sink(arg: e4!) // $ flow=503 + sink(arg: e5!) + sink(arg: e6!) // $ flow=505 } func source2() -> (Int, Int)? { return nil } @@ -523,8 +554,8 @@ func testOptionalPropertyAccess(y: Int?) { } func testIdentityArithmetic() { - sink(arg: +source()) // $ flow=526 - sink(arg: (source())) // $ flow=527 + sink(arg: +source()) // $ flow=557 + sink(arg: (source())) // $ flow=558 } func sink(str: String) {} @@ -541,13 +572,13 @@ class MyClass { extension MyClass { convenience init(contentsOfFile: String) { self.init(s: source3()) - sink(str: str) // $ flow=543 + sink(str: str) // $ flow=574 } } func extensionInits(path: String) { - sink(str: MyClass(s: source3()).str) // $ flow=549 - sink(str: MyClass(contentsOfFile: path).str) // $ flow=543 + sink(str: MyClass(s: source3()).str) // $ flow=580 + sink(str: MyClass(contentsOfFile: path).str) // $ flow=574 } class InoutConstructorClass { @@ -572,10 +603,10 @@ struct S { func testKeyPath() { let s = S(x: source()) let f = \S.x - sink(arg: s[keyPath: f]) // $ flow=573 + sink(arg: s[keyPath: f]) // $ flow=604 let inferred : KeyPath = \.x - sink(arg: s[keyPath: inferred]) // $ flow=573 + sink(arg: s[keyPath: inferred]) // $ flow=604 } struct S2 { @@ -590,13 +621,13 @@ func testNestedKeyPath() { let s = S(x: source()) let s2 = S2(s: s) let f = \S2.s.x - sink(arg: s2[keyPath: f]) // $ flow=590 + sink(arg: s2[keyPath: f]) // $ flow=621 } func testArrayKeyPath() { let array = [source()] let f = \[Int].[0] - sink(arg: array[keyPath: f]) // $ MISSING: flow=597 + sink(arg: array[keyPath: f]) // $ flow=628 } struct S2_Optional { @@ -611,7 +642,7 @@ func testOptionalKeyPath() { let s = S(x: source()) let s2 = S2_Optional(s: s) let f = \S2_Optional.s?.x - sink(opt: s2[keyPath: f]) // $ MISSING: flow=611 + sink(opt: s2[keyPath: f]) // $ MISSING: flow=642 } func testSwap() { @@ -623,11 +654,42 @@ func testSwap() { x = y y = t sink(arg: x) - sink(arg: y) // $ flow=618 + sink(arg: y) // $ flow=649 x = source() y = 0 swap(&x, &y) - sink(arg: x) // $ SPURIOUS: flow=628 - sink(arg: y) // $ flow=628 + sink(arg: x) // $ SPURIOUS: flow=659 + sink(arg: y) // $ flow=659 +} + +func testArray() { + var arr1 = [1,2,3] + sink(arg: arr1[0]) + arr1[1] = source() + sink(arg: arr1[0]) // $ flow=669 + sink(arg: arr1) + + var arr2 = [source()] + sink(arg: arr2[0]) // $ flow=673 + + var matrix = [[source()]] + sink(arg: matrix[0]) + sink(arg: matrix[0][0]) // $ flow=676 + + var matrix2 = [[1]] + matrix2[0][0] = source() + sink(arg: matrix2[0][0]) // $ flow=681 + + var arr3 = [1] + var arr4 = arr2 + arr3 + sink(arg: arr3[0]) + sink(arg: arr4[0]) // $ MISSING: flow=673 + + var arr5 = Array(repeating: source(), count: 2) + sink(arg: arr5[0]) // $ MISSING: flow=689 + + var arr6 = [1,2,3] + arr6.insert(source(), at: 2) + sink(arg: arr6[0]) // $ flow=693 } diff --git a/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected new file mode 100644 index 00000000000..ac216c004f2 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected @@ -0,0 +1,3 @@ +deadEnd +| customurlschemes.swift:94:59:94:76 | options | +| customurlschemes.swift:133:59:133:76 | options | diff --git a/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql b/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql index 71e6055b7d4..e4ea2e0e56e 100644 --- a/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql +++ b/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql @@ -1,6 +1,21 @@ import swift import TestUtilities.InlineExpectationsTest import FlowConfig +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.DataFlow + +module TestConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src instanceof FlowSource } + + predicate isSink(DataFlow::Node sink) { + exists(CallExpr sinkCall | + sinkCall.getStaticTarget().getName().matches("sink%") and + sinkCall.getAnArgument().getExpr() = sink.asExpr() + ) + } +} + +module TestFlow = TaintTracking::Global; string describe(FlowSource source) { source instanceof RemoteFlowSource and result = "remote" @@ -9,7 +24,7 @@ string describe(FlowSource source) { } module FlowSourcesTest implements TestSig { - string getARelevantTag() { result = "source" } + string getARelevantTag() { result = ["source", "tainted"] } predicate hasActualResult(Location location, string element, string tag, string value) { exists(FlowSource source | @@ -19,6 +34,16 @@ module FlowSourcesTest implements TestSig { tag = "source" and value = describe(source) ) + or + exists(DataFlow::Node sink | + // this is not really what the "flowsources" test is about, but sometimes it's helpful to + // have sinks and confirm that taint reaches obvious points in the flow source test code. + TestFlow::flowTo(sink) and + location = sink.getLocation() and + element = sink.toString() and + tag = "tainted" and + value = "" + ) } } diff --git a/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift b/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift index 4a300dcd217..eb50c15824b 100644 --- a/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift +++ b/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift @@ -26,12 +26,24 @@ protocol UIApplicationDelegate { } class UIScene { - class ConnectionOptions {} + class ConnectionOptions { + var userActivities: Set { get { return Set() } } + var urlContexts: Set { get { return Set() } } + } } class UISceneSession {} -class NSUserActivity {} +class NSUserActivity: Hashable { + static func == (lhs: NSUserActivity, rhs: NSUserActivity) -> Bool { + return true; + } + + func hash(into hasher: inout Hasher) {} + + var webpageURL: URL? { get { return nil } set { } } + var referrerURL: URL? { get { return nil } set { } } +} class UIOpenURLContext: Hashable { static func == (lhs: UIOpenURLContext, rhs: UIOpenURLContext) -> Bool { @@ -39,6 +51,8 @@ class UIOpenURLContext: Hashable { } func hash(into hasher: inout Hasher) {} + + var url: URL { get { return URL() } } } protocol UISceneDelegate { @@ -48,6 +62,8 @@ protocol UISceneDelegate { func scene(_: UIScene, openURLContexts: Set) } +func sink(arg: Any) {} + // --- tests --- class AppDelegate: UIApplicationDelegate { @@ -64,28 +80,88 @@ class AppDelegate: UIApplicationDelegate { } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { - launchOptions?[.url] // $ source=remote + _ = launchOptions?[.url] // $ source=remote return true } func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { - launchOptions?[.url] // $ source=remote + _ = launchOptions?[.url] // $ source=remote return true } } class SceneDelegate : UISceneDelegate { - func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) {} // $ source=remote - func scene(_: UIScene, continue: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, didUpdate: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, openURLContexts: Set) {} // $ source=remote + func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) { // $ source=remote + for userActivity in options.userActivities { + let x = userActivity.webpageURL + sink(arg: x) // $ MISSING: tainted + let y = userActivity.referrerURL + sink(arg: y) // $ MISSING: tainted + } + + for urlContext in options.urlContexts { + let z = urlContext.url + sink(arg: z) // $ MISSING: tainted + } + } + + func scene(_: UIScene, continue: NSUserActivity) { // $ source=remote + let x = `continue`.webpageURL + sink(arg: x) // $ tainted + let y = `continue`.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, didUpdate: NSUserActivity) { // $ source=remote + let x = didUpdate.webpageURL + sink(arg: x) // $ tainted + let y = didUpdate.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, openURLContexts: Set) { // $ source=remote + for openURLContext in openURLContexts { + let x = openURLContext.url + sink(arg: x) // $ MISSING: tainted + } + } } class Extended {} extension Extended : UISceneDelegate { - func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) {} // $ source=remote - func scene(_: UIScene, continue: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, didUpdate: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, openURLContexts: Set) {} // $ source=remote + func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) { // $ source=remote + for userActivity in options.userActivities { + let x = userActivity.webpageURL + sink(arg: x) // $ MISSING: tainted + let y = userActivity.referrerURL + sink(arg: y) // $ MISSING: tainted + } + + for urlContext in options.urlContexts { + let z = urlContext.url + sink(arg: z) // $ MISSING: tainted + } + } + + func scene(_: UIScene, continue: NSUserActivity) { // $ source=remote + let x = `continue`.webpageURL + sink(arg: x) // $ tainted + let y = `continue`.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, didUpdate: NSUserActivity) { // $ source=remote + let x = didUpdate.webpageURL + sink(arg: x) // $ tainted + let y = didUpdate.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, openURLContexts: Set) { // $ source=remote + for openURLContext in openURLContexts { + let x = openURLContext.url + sink(arg: x) // $ MISSING: tainted + } + } } diff --git a/swift/ql/test/library-tests/dataflow/taint/core/LocalTaint.expected b/swift/ql/test/library-tests/dataflow/taint/core/LocalTaint.expected index 9986d248b84..b7408d1f57e 100644 --- a/swift/ql/test/library-tests/dataflow/taint/core/LocalTaint.expected +++ b/swift/ql/test/library-tests/dataflow/taint/core/LocalTaint.expected @@ -37,75 +37,75 @@ | simple.swift:37:13:37:13 | [post] a | simple.swift:38:3:38:3 | a | | simple.swift:37:13:37:13 | a | simple.swift:38:3:38:3 | a | | simple.swift:38:3:38:3 | &... | simple.swift:39:13:39:13 | a | -| simple.swift:38:3:38:3 | [post] &... | simple.swift:39:13:39:13 | a | +| simple.swift:38:3:38:3 | [post] a | simple.swift:38:3:38:3 | &... | | simple.swift:38:3:38:3 | a | simple.swift:38:3:38:3 | &... | -| simple.swift:38:8:38:8 | 1 | simple.swift:38:3:38:3 | &... | +| simple.swift:38:8:38:8 | 1 | simple.swift:38:3:38:3 | a | | simple.swift:39:13:39:13 | [post] a | simple.swift:40:3:40:3 | a | | simple.swift:39:13:39:13 | a | simple.swift:40:3:40:3 | a | | simple.swift:40:3:40:3 | &... | simple.swift:41:13:41:13 | a | -| simple.swift:40:3:40:3 | [post] &... | simple.swift:41:13:41:13 | a | +| simple.swift:40:3:40:3 | [post] a | simple.swift:40:3:40:3 | &... | | simple.swift:40:3:40:3 | a | simple.swift:40:3:40:3 | &... | -| simple.swift:40:8:40:15 | call to source() | simple.swift:40:3:40:3 | &... | +| simple.swift:40:8:40:15 | call to source() | simple.swift:40:3:40:3 | a | | simple.swift:41:13:41:13 | [post] a | simple.swift:42:3:42:3 | a | | simple.swift:41:13:41:13 | a | simple.swift:42:3:42:3 | a | | simple.swift:42:3:42:3 | &... | simple.swift:43:13:43:13 | a | -| simple.swift:42:3:42:3 | [post] &... | simple.swift:43:13:43:13 | a | +| simple.swift:42:3:42:3 | [post] a | simple.swift:42:3:42:3 | &... | | simple.swift:42:3:42:3 | a | simple.swift:42:3:42:3 | &... | -| simple.swift:42:8:42:8 | 1 | simple.swift:42:3:42:3 | &... | +| simple.swift:42:8:42:8 | 1 | simple.swift:42:3:42:3 | a | | simple.swift:44:3:44:7 | SSA def(a) | simple.swift:45:13:45:13 | a | | simple.swift:44:7:44:7 | 0 | simple.swift:44:3:44:7 | SSA def(a) | | simple.swift:47:7:47:7 | SSA def(b) | simple.swift:48:3:48:3 | b | | simple.swift:47:7:47:7 | b | simple.swift:47:7:47:7 | SSA def(b) | | simple.swift:47:11:47:11 | 128 | simple.swift:47:7:47:7 | b | | simple.swift:48:3:48:3 | &... | simple.swift:49:13:49:13 | b | -| simple.swift:48:3:48:3 | [post] &... | simple.swift:49:13:49:13 | b | +| simple.swift:48:3:48:3 | [post] b | simple.swift:48:3:48:3 | &... | | simple.swift:48:3:48:3 | b | simple.swift:48:3:48:3 | &... | -| simple.swift:48:8:48:15 | call to source() | simple.swift:48:3:48:3 | &... | +| simple.swift:48:8:48:15 | call to source() | simple.swift:48:3:48:3 | b | | simple.swift:49:13:49:13 | [post] b | simple.swift:50:3:50:3 | b | | simple.swift:49:13:49:13 | b | simple.swift:50:3:50:3 | b | | simple.swift:50:3:50:3 | &... | simple.swift:51:13:51:13 | b | -| simple.swift:50:3:50:3 | [post] &... | simple.swift:51:13:51:13 | b | +| simple.swift:50:3:50:3 | [post] b | simple.swift:50:3:50:3 | &... | | simple.swift:50:3:50:3 | b | simple.swift:50:3:50:3 | &... | -| simple.swift:50:8:50:8 | 1 | simple.swift:50:3:50:3 | &... | +| simple.swift:50:8:50:8 | 1 | simple.swift:50:3:50:3 | b | | simple.swift:53:7:53:7 | SSA def(c) | simple.swift:54:3:54:3 | c | | simple.swift:53:7:53:7 | c | simple.swift:53:7:53:7 | SSA def(c) | | simple.swift:53:11:53:11 | 10 | simple.swift:53:7:53:7 | c | | simple.swift:54:3:54:3 | &... | simple.swift:55:13:55:13 | c | -| simple.swift:54:3:54:3 | [post] &... | simple.swift:55:13:55:13 | c | +| simple.swift:54:3:54:3 | [post] c | simple.swift:54:3:54:3 | &... | | simple.swift:54:3:54:3 | c | simple.swift:54:3:54:3 | &... | -| simple.swift:54:8:54:15 | call to source() | simple.swift:54:3:54:3 | &... | +| simple.swift:54:8:54:15 | call to source() | simple.swift:54:3:54:3 | c | | simple.swift:55:13:55:13 | [post] c | simple.swift:56:3:56:3 | c | | simple.swift:55:13:55:13 | c | simple.swift:56:3:56:3 | c | | simple.swift:56:3:56:3 | &... | simple.swift:57:13:57:13 | c | -| simple.swift:56:3:56:3 | [post] &... | simple.swift:57:13:57:13 | c | +| simple.swift:56:3:56:3 | [post] c | simple.swift:56:3:56:3 | &... | | simple.swift:56:3:56:3 | c | simple.swift:56:3:56:3 | &... | -| simple.swift:56:8:56:8 | 2 | simple.swift:56:3:56:3 | &... | +| simple.swift:56:8:56:8 | 2 | simple.swift:56:3:56:3 | c | | simple.swift:59:7:59:7 | SSA def(d) | simple.swift:60:3:60:3 | d | | simple.swift:59:7:59:7 | d | simple.swift:59:7:59:7 | SSA def(d) | | simple.swift:59:11:59:11 | 100 | simple.swift:59:7:59:7 | d | | simple.swift:60:3:60:3 | &... | simple.swift:61:13:61:13 | d | -| simple.swift:60:3:60:3 | [post] &... | simple.swift:61:13:61:13 | d | +| simple.swift:60:3:60:3 | [post] d | simple.swift:60:3:60:3 | &... | | simple.swift:60:3:60:3 | d | simple.swift:60:3:60:3 | &... | -| simple.swift:60:8:60:15 | call to source() | simple.swift:60:3:60:3 | &... | +| simple.swift:60:8:60:15 | call to source() | simple.swift:60:3:60:3 | d | | simple.swift:61:13:61:13 | [post] d | simple.swift:62:3:62:3 | d | | simple.swift:61:13:61:13 | d | simple.swift:62:3:62:3 | d | | simple.swift:62:3:62:3 | &... | simple.swift:63:13:63:13 | d | -| simple.swift:62:3:62:3 | [post] &... | simple.swift:63:13:63:13 | d | +| simple.swift:62:3:62:3 | [post] d | simple.swift:62:3:62:3 | &... | | simple.swift:62:3:62:3 | d | simple.swift:62:3:62:3 | &... | -| simple.swift:62:8:62:8 | 2 | simple.swift:62:3:62:3 | &... | +| simple.swift:62:8:62:8 | 2 | simple.swift:62:3:62:3 | d | | simple.swift:65:7:65:7 | SSA def(e) | simple.swift:66:3:66:3 | e | | simple.swift:65:7:65:7 | e | simple.swift:65:7:65:7 | SSA def(e) | | simple.swift:65:11:65:11 | 1000 | simple.swift:65:7:65:7 | e | | simple.swift:66:3:66:3 | &... | simple.swift:67:13:67:13 | e | -| simple.swift:66:3:66:3 | [post] &... | simple.swift:67:13:67:13 | e | +| simple.swift:66:3:66:3 | [post] e | simple.swift:66:3:66:3 | &... | | simple.swift:66:3:66:3 | e | simple.swift:66:3:66:3 | &... | -| simple.swift:66:8:66:15 | call to source() | simple.swift:66:3:66:3 | &... | +| simple.swift:66:8:66:15 | call to source() | simple.swift:66:3:66:3 | e | | simple.swift:67:13:67:13 | [post] e | simple.swift:68:3:68:3 | e | | simple.swift:67:13:67:13 | e | simple.swift:68:3:68:3 | e | | simple.swift:68:3:68:3 | &... | simple.swift:69:13:69:13 | e | -| simple.swift:68:3:68:3 | [post] &... | simple.swift:69:13:69:13 | e | +| simple.swift:68:3:68:3 | [post] e | simple.swift:68:3:68:3 | &... | | simple.swift:68:3:68:3 | e | simple.swift:68:3:68:3 | &... | -| simple.swift:68:8:68:8 | 100 | simple.swift:68:3:68:3 | &... | +| simple.swift:68:8:68:8 | 100 | simple.swift:68:3:68:3 | e | | simple.swift:73:13:73:13 | 0 | simple.swift:73:13:73:24 | ... .\|(_:_:) ... | | simple.swift:73:17:73:24 | call to source() | simple.swift:73:13:73:24 | ... .\|(_:_:) ... | | simple.swift:74:13:74:20 | call to source() | simple.swift:74:13:74:24 | ... .\|(_:_:) ... | @@ -144,33 +144,33 @@ | stringinterpolation.swift:11:36:11:44 | SSA def(pair) | stringinterpolation.swift:13:36:13:36 | pair | | stringinterpolation.swift:11:36:11:44 | pair | stringinterpolation.swift:11:36:11:44 | SSA def(pair) | | stringinterpolation.swift:13:3:13:3 | &... | stringinterpolation.swift:11:11:14:2 | self[return] | -| stringinterpolation.swift:13:3:13:3 | &... | stringinterpolation.swift:13:3:13:3 | [post] &... | -| stringinterpolation.swift:13:3:13:3 | &... | stringinterpolation.swift:13:23:13:23 | [post] "..." | -| stringinterpolation.swift:13:3:13:3 | [post] &... | stringinterpolation.swift:11:11:14:2 | self[return] | +| stringinterpolation.swift:13:3:13:3 | [post] self | stringinterpolation.swift:13:3:13:3 | &... | | stringinterpolation.swift:13:3:13:3 | self | stringinterpolation.swift:13:3:13:3 | &... | -| stringinterpolation.swift:13:23:13:23 | "..." | stringinterpolation.swift:13:3:13:3 | [post] &... | +| stringinterpolation.swift:13:3:13:3 | self | stringinterpolation.swift:13:3:13:3 | [post] self | +| stringinterpolation.swift:13:3:13:3 | self | stringinterpolation.swift:13:23:13:23 | [post] "..." | +| stringinterpolation.swift:13:23:13:23 | "..." | stringinterpolation.swift:13:3:13:3 | [post] self | | stringinterpolation.swift:13:23:13:23 | "..." | stringinterpolation.swift:13:23:13:23 | [post] "..." | | stringinterpolation.swift:13:23:13:23 | SSA def($interpolation) | stringinterpolation.swift:13:24:13:24 | $interpolation | | stringinterpolation.swift:13:23:13:23 | TapExpr | stringinterpolation.swift:13:23:13:23 | "..." | | stringinterpolation.swift:13:23:13:23 | first is: | stringinterpolation.swift:13:23:13:23 | [post] first is: | -| stringinterpolation.swift:13:23:13:23 | first is: | stringinterpolation.swift:13:24:13:24 | [post] &... | +| stringinterpolation.swift:13:23:13:23 | first is: | stringinterpolation.swift:13:24:13:24 | [post] $interpolation | +| stringinterpolation.swift:13:24:13:24 | $interpolation | stringinterpolation.swift:13:23:13:23 | [post] first is: | | stringinterpolation.swift:13:24:13:24 | $interpolation | stringinterpolation.swift:13:24:13:24 | &... | -| stringinterpolation.swift:13:24:13:24 | &... | stringinterpolation.swift:13:23:13:23 | [post] first is: | -| stringinterpolation.swift:13:24:13:24 | &... | stringinterpolation.swift:13:24:13:24 | [post] &... | +| stringinterpolation.swift:13:24:13:24 | $interpolation | stringinterpolation.swift:13:24:13:24 | [post] $interpolation | | stringinterpolation.swift:13:24:13:24 | &... | stringinterpolation.swift:13:35:13:35 | $interpolation | -| stringinterpolation.swift:13:24:13:24 | [post] &... | stringinterpolation.swift:13:35:13:35 | $interpolation | +| stringinterpolation.swift:13:24:13:24 | [post] $interpolation | stringinterpolation.swift:13:24:13:24 | &... | | stringinterpolation.swift:13:35:13:35 | $interpolation | stringinterpolation.swift:13:35:13:35 | &... | -| stringinterpolation.swift:13:35:13:35 | &... | stringinterpolation.swift:13:35:13:35 | [post] &... | +| stringinterpolation.swift:13:35:13:35 | $interpolation | stringinterpolation.swift:13:35:13:35 | [post] $interpolation | | stringinterpolation.swift:13:35:13:35 | &... | stringinterpolation.swift:13:47:13:47 | $interpolation | -| stringinterpolation.swift:13:35:13:35 | [post] &... | stringinterpolation.swift:13:47:13:47 | $interpolation | -| stringinterpolation.swift:13:36:13:41 | .first | stringinterpolation.swift:13:35:13:35 | [post] &... | +| stringinterpolation.swift:13:35:13:35 | [post] $interpolation | stringinterpolation.swift:13:35:13:35 | &... | +| stringinterpolation.swift:13:36:13:41 | .first | stringinterpolation.swift:13:35:13:35 | [post] $interpolation | | stringinterpolation.swift:13:47:13:47 | | stringinterpolation.swift:13:47:13:47 | [post] | -| stringinterpolation.swift:13:47:13:47 | | stringinterpolation.swift:13:47:13:47 | [post] &... | +| stringinterpolation.swift:13:47:13:47 | | stringinterpolation.swift:13:47:13:47 | [post] $interpolation | | stringinterpolation.swift:13:47:13:47 | $interpolation | stringinterpolation.swift:13:47:13:47 | &... | +| stringinterpolation.swift:13:47:13:47 | $interpolation | stringinterpolation.swift:13:47:13:47 | [post] | +| stringinterpolation.swift:13:47:13:47 | $interpolation | stringinterpolation.swift:13:47:13:47 | [post] $interpolation | | stringinterpolation.swift:13:47:13:47 | &... | stringinterpolation.swift:13:23:13:23 | TapExpr | -| stringinterpolation.swift:13:47:13:47 | &... | stringinterpolation.swift:13:47:13:47 | [post] | -| stringinterpolation.swift:13:47:13:47 | &... | stringinterpolation.swift:13:47:13:47 | [post] &... | -| stringinterpolation.swift:13:47:13:47 | [post] &... | stringinterpolation.swift:13:23:13:23 | TapExpr | +| stringinterpolation.swift:13:47:13:47 | [post] $interpolation | stringinterpolation.swift:13:47:13:47 | &... | | stringinterpolation.swift:18:6:18:6 | SSA def(p1) | stringinterpolation.swift:19:2:19:2 | p1 | | stringinterpolation.swift:18:6:18:6 | p1 | stringinterpolation.swift:18:6:18:6 | SSA def(p1) | | stringinterpolation.swift:18:11:18:24 | call to MyStringPair.init() | stringinterpolation.swift:18:6:18:6 | p1 | @@ -181,72 +181,72 @@ | stringinterpolation.swift:22:12:22:12 | SSA def($interpolation) | stringinterpolation.swift:22:13:22:13 | $interpolation | | stringinterpolation.swift:22:12:22:12 | TapExpr | stringinterpolation.swift:22:12:22:12 | "..." | | stringinterpolation.swift:22:12:22:12 | pair: | stringinterpolation.swift:22:12:22:12 | [post] pair: | -| stringinterpolation.swift:22:12:22:12 | pair: | stringinterpolation.swift:22:13:22:13 | [post] &... | +| stringinterpolation.swift:22:12:22:12 | pair: | stringinterpolation.swift:22:13:22:13 | [post] $interpolation | +| stringinterpolation.swift:22:13:22:13 | $interpolation | stringinterpolation.swift:22:12:22:12 | [post] pair: | | stringinterpolation.swift:22:13:22:13 | $interpolation | stringinterpolation.swift:22:13:22:13 | &... | -| stringinterpolation.swift:22:13:22:13 | &... | stringinterpolation.swift:22:12:22:12 | [post] pair: | -| stringinterpolation.swift:22:13:22:13 | &... | stringinterpolation.swift:22:13:22:13 | [post] &... | +| stringinterpolation.swift:22:13:22:13 | $interpolation | stringinterpolation.swift:22:13:22:13 | [post] $interpolation | | stringinterpolation.swift:22:13:22:13 | &... | stringinterpolation.swift:22:20:22:20 | $interpolation | -| stringinterpolation.swift:22:13:22:13 | [post] &... | stringinterpolation.swift:22:20:22:20 | $interpolation | +| stringinterpolation.swift:22:13:22:13 | [post] $interpolation | stringinterpolation.swift:22:13:22:13 | &... | | stringinterpolation.swift:22:20:22:20 | $interpolation | stringinterpolation.swift:22:20:22:20 | &... | -| stringinterpolation.swift:22:20:22:20 | &... | stringinterpolation.swift:22:20:22:20 | [post] &... | +| stringinterpolation.swift:22:20:22:20 | $interpolation | stringinterpolation.swift:22:20:22:20 | [post] $interpolation | | stringinterpolation.swift:22:20:22:20 | &... | stringinterpolation.swift:22:30:22:30 | $interpolation | -| stringinterpolation.swift:22:20:22:20 | [post] &... | stringinterpolation.swift:22:30:22:30 | $interpolation | +| stringinterpolation.swift:22:20:22:20 | [post] $interpolation | stringinterpolation.swift:22:20:22:20 | &... | | stringinterpolation.swift:22:21:22:21 | [post] p1 | stringinterpolation.swift:23:21:23:21 | p1 | | stringinterpolation.swift:22:21:22:21 | p1 | stringinterpolation.swift:23:21:23:21 | p1 | -| stringinterpolation.swift:22:21:22:24 | .first | stringinterpolation.swift:22:20:22:20 | [post] &... | +| stringinterpolation.swift:22:21:22:24 | .first | stringinterpolation.swift:22:20:22:20 | [post] $interpolation | | stringinterpolation.swift:22:30:22:30 | | stringinterpolation.swift:22:30:22:30 | [post] | -| stringinterpolation.swift:22:30:22:30 | | stringinterpolation.swift:22:30:22:30 | [post] &... | +| stringinterpolation.swift:22:30:22:30 | | stringinterpolation.swift:22:30:22:30 | [post] $interpolation | | stringinterpolation.swift:22:30:22:30 | $interpolation | stringinterpolation.swift:22:30:22:30 | &... | +| stringinterpolation.swift:22:30:22:30 | $interpolation | stringinterpolation.swift:22:30:22:30 | [post] | +| stringinterpolation.swift:22:30:22:30 | $interpolation | stringinterpolation.swift:22:30:22:30 | [post] $interpolation | | stringinterpolation.swift:22:30:22:30 | &... | stringinterpolation.swift:22:12:22:12 | TapExpr | -| stringinterpolation.swift:22:30:22:30 | &... | stringinterpolation.swift:22:30:22:30 | [post] | -| stringinterpolation.swift:22:30:22:30 | &... | stringinterpolation.swift:22:30:22:30 | [post] &... | -| stringinterpolation.swift:22:30:22:30 | [post] &... | stringinterpolation.swift:22:12:22:12 | TapExpr | +| stringinterpolation.swift:22:30:22:30 | [post] $interpolation | stringinterpolation.swift:22:30:22:30 | &... | | stringinterpolation.swift:23:12:23:12 | SSA def($interpolation) | stringinterpolation.swift:23:13:23:13 | $interpolation | | stringinterpolation.swift:23:12:23:12 | TapExpr | stringinterpolation.swift:23:12:23:12 | "..." | | stringinterpolation.swift:23:12:23:12 | pair: | stringinterpolation.swift:23:12:23:12 | [post] pair: | -| stringinterpolation.swift:23:12:23:12 | pair: | stringinterpolation.swift:23:13:23:13 | [post] &... | +| stringinterpolation.swift:23:12:23:12 | pair: | stringinterpolation.swift:23:13:23:13 | [post] $interpolation | +| stringinterpolation.swift:23:13:23:13 | $interpolation | stringinterpolation.swift:23:12:23:12 | [post] pair: | | stringinterpolation.swift:23:13:23:13 | $interpolation | stringinterpolation.swift:23:13:23:13 | &... | -| stringinterpolation.swift:23:13:23:13 | &... | stringinterpolation.swift:23:12:23:12 | [post] pair: | -| stringinterpolation.swift:23:13:23:13 | &... | stringinterpolation.swift:23:13:23:13 | [post] &... | +| stringinterpolation.swift:23:13:23:13 | $interpolation | stringinterpolation.swift:23:13:23:13 | [post] $interpolation | | stringinterpolation.swift:23:13:23:13 | &... | stringinterpolation.swift:23:20:23:20 | $interpolation | -| stringinterpolation.swift:23:13:23:13 | [post] &... | stringinterpolation.swift:23:20:23:20 | $interpolation | +| stringinterpolation.swift:23:13:23:13 | [post] $interpolation | stringinterpolation.swift:23:13:23:13 | &... | | stringinterpolation.swift:23:20:23:20 | $interpolation | stringinterpolation.swift:23:20:23:20 | &... | -| stringinterpolation.swift:23:20:23:20 | &... | stringinterpolation.swift:23:20:23:20 | [post] &... | +| stringinterpolation.swift:23:20:23:20 | $interpolation | stringinterpolation.swift:23:20:23:20 | [post] $interpolation | | stringinterpolation.swift:23:20:23:20 | &... | stringinterpolation.swift:23:31:23:31 | $interpolation | -| stringinterpolation.swift:23:20:23:20 | [post] &... | stringinterpolation.swift:23:31:23:31 | $interpolation | +| stringinterpolation.swift:23:20:23:20 | [post] $interpolation | stringinterpolation.swift:23:20:23:20 | &... | | stringinterpolation.swift:23:21:23:21 | [post] p1 | stringinterpolation.swift:24:21:24:21 | p1 | | stringinterpolation.swift:23:21:23:21 | p1 | stringinterpolation.swift:24:21:24:21 | p1 | -| stringinterpolation.swift:23:21:23:24 | .second | stringinterpolation.swift:23:20:23:20 | [post] &... | +| stringinterpolation.swift:23:21:23:24 | .second | stringinterpolation.swift:23:20:23:20 | [post] $interpolation | | stringinterpolation.swift:23:31:23:31 | | stringinterpolation.swift:23:31:23:31 | [post] | -| stringinterpolation.swift:23:31:23:31 | | stringinterpolation.swift:23:31:23:31 | [post] &... | +| stringinterpolation.swift:23:31:23:31 | | stringinterpolation.swift:23:31:23:31 | [post] $interpolation | | stringinterpolation.swift:23:31:23:31 | $interpolation | stringinterpolation.swift:23:31:23:31 | &... | +| stringinterpolation.swift:23:31:23:31 | $interpolation | stringinterpolation.swift:23:31:23:31 | [post] | +| stringinterpolation.swift:23:31:23:31 | $interpolation | stringinterpolation.swift:23:31:23:31 | [post] $interpolation | | stringinterpolation.swift:23:31:23:31 | &... | stringinterpolation.swift:23:12:23:12 | TapExpr | -| stringinterpolation.swift:23:31:23:31 | &... | stringinterpolation.swift:23:31:23:31 | [post] | -| stringinterpolation.swift:23:31:23:31 | &... | stringinterpolation.swift:23:31:23:31 | [post] &... | -| stringinterpolation.swift:23:31:23:31 | [post] &... | stringinterpolation.swift:23:12:23:12 | TapExpr | +| stringinterpolation.swift:23:31:23:31 | [post] $interpolation | stringinterpolation.swift:23:31:23:31 | &... | | stringinterpolation.swift:24:12:24:12 | SSA def($interpolation) | stringinterpolation.swift:24:13:24:13 | $interpolation | | stringinterpolation.swift:24:12:24:12 | TapExpr | stringinterpolation.swift:24:12:24:12 | "..." | | stringinterpolation.swift:24:12:24:12 | pair: | stringinterpolation.swift:24:12:24:12 | [post] pair: | -| stringinterpolation.swift:24:12:24:12 | pair: | stringinterpolation.swift:24:13:24:13 | [post] &... | +| stringinterpolation.swift:24:12:24:12 | pair: | stringinterpolation.swift:24:13:24:13 | [post] $interpolation | +| stringinterpolation.swift:24:13:24:13 | $interpolation | stringinterpolation.swift:24:12:24:12 | [post] pair: | | stringinterpolation.swift:24:13:24:13 | $interpolation | stringinterpolation.swift:24:13:24:13 | &... | -| stringinterpolation.swift:24:13:24:13 | &... | stringinterpolation.swift:24:12:24:12 | [post] pair: | -| stringinterpolation.swift:24:13:24:13 | &... | stringinterpolation.swift:24:13:24:13 | [post] &... | +| stringinterpolation.swift:24:13:24:13 | $interpolation | stringinterpolation.swift:24:13:24:13 | [post] $interpolation | | stringinterpolation.swift:24:13:24:13 | &... | stringinterpolation.swift:24:20:24:20 | $interpolation | -| stringinterpolation.swift:24:13:24:13 | [post] &... | stringinterpolation.swift:24:20:24:20 | $interpolation | +| stringinterpolation.swift:24:13:24:13 | [post] $interpolation | stringinterpolation.swift:24:13:24:13 | &... | | stringinterpolation.swift:24:20:24:20 | $interpolation | stringinterpolation.swift:24:20:24:20 | &... | -| stringinterpolation.swift:24:20:24:20 | &... | stringinterpolation.swift:24:20:24:20 | [post] &... | -| stringinterpolation.swift:24:20:24:20 | &... | stringinterpolation.swift:24:21:24:21 | [post] p1 | +| stringinterpolation.swift:24:20:24:20 | $interpolation | stringinterpolation.swift:24:20:24:20 | [post] $interpolation | +| stringinterpolation.swift:24:20:24:20 | $interpolation | stringinterpolation.swift:24:21:24:21 | [post] p1 | | stringinterpolation.swift:24:20:24:20 | &... | stringinterpolation.swift:24:24:24:24 | $interpolation | -| stringinterpolation.swift:24:20:24:20 | [post] &... | stringinterpolation.swift:24:24:24:24 | $interpolation | -| stringinterpolation.swift:24:21:24:21 | p1 | stringinterpolation.swift:24:20:24:20 | [post] &... | +| stringinterpolation.swift:24:20:24:20 | [post] $interpolation | stringinterpolation.swift:24:20:24:20 | &... | +| stringinterpolation.swift:24:21:24:21 | p1 | stringinterpolation.swift:24:20:24:20 | [post] $interpolation | | stringinterpolation.swift:24:21:24:21 | p1 | stringinterpolation.swift:24:21:24:21 | [post] p1 | | stringinterpolation.swift:24:24:24:24 | | stringinterpolation.swift:24:24:24:24 | [post] | -| stringinterpolation.swift:24:24:24:24 | | stringinterpolation.swift:24:24:24:24 | [post] &... | +| stringinterpolation.swift:24:24:24:24 | | stringinterpolation.swift:24:24:24:24 | [post] $interpolation | | stringinterpolation.swift:24:24:24:24 | $interpolation | stringinterpolation.swift:24:24:24:24 | &... | +| stringinterpolation.swift:24:24:24:24 | $interpolation | stringinterpolation.swift:24:24:24:24 | [post] | +| stringinterpolation.swift:24:24:24:24 | $interpolation | stringinterpolation.swift:24:24:24:24 | [post] $interpolation | | stringinterpolation.swift:24:24:24:24 | &... | stringinterpolation.swift:24:12:24:12 | TapExpr | -| stringinterpolation.swift:24:24:24:24 | &... | stringinterpolation.swift:24:24:24:24 | [post] | -| stringinterpolation.swift:24:24:24:24 | &... | stringinterpolation.swift:24:24:24:24 | [post] &... | -| stringinterpolation.swift:24:24:24:24 | [post] &... | stringinterpolation.swift:24:12:24:12 | TapExpr | +| stringinterpolation.swift:24:24:24:24 | [post] $interpolation | stringinterpolation.swift:24:24:24:24 | &... | | stringinterpolation.swift:26:6:26:6 | SSA def(p2) | stringinterpolation.swift:27:2:27:2 | p2 | | stringinterpolation.swift:26:6:26:6 | p2 | stringinterpolation.swift:26:6:26:6 | SSA def(p2) | | stringinterpolation.swift:26:11:26:24 | call to MyStringPair.init() | stringinterpolation.swift:26:6:26:6 | p2 | @@ -257,72 +257,72 @@ | stringinterpolation.swift:30:12:30:12 | SSA def($interpolation) | stringinterpolation.swift:30:13:30:13 | $interpolation | | stringinterpolation.swift:30:12:30:12 | TapExpr | stringinterpolation.swift:30:12:30:12 | "..." | | stringinterpolation.swift:30:12:30:12 | pair: | stringinterpolation.swift:30:12:30:12 | [post] pair: | -| stringinterpolation.swift:30:12:30:12 | pair: | stringinterpolation.swift:30:13:30:13 | [post] &... | +| stringinterpolation.swift:30:12:30:12 | pair: | stringinterpolation.swift:30:13:30:13 | [post] $interpolation | +| stringinterpolation.swift:30:13:30:13 | $interpolation | stringinterpolation.swift:30:12:30:12 | [post] pair: | | stringinterpolation.swift:30:13:30:13 | $interpolation | stringinterpolation.swift:30:13:30:13 | &... | -| stringinterpolation.swift:30:13:30:13 | &... | stringinterpolation.swift:30:12:30:12 | [post] pair: | -| stringinterpolation.swift:30:13:30:13 | &... | stringinterpolation.swift:30:13:30:13 | [post] &... | +| stringinterpolation.swift:30:13:30:13 | $interpolation | stringinterpolation.swift:30:13:30:13 | [post] $interpolation | | stringinterpolation.swift:30:13:30:13 | &... | stringinterpolation.swift:30:20:30:20 | $interpolation | -| stringinterpolation.swift:30:13:30:13 | [post] &... | stringinterpolation.swift:30:20:30:20 | $interpolation | +| stringinterpolation.swift:30:13:30:13 | [post] $interpolation | stringinterpolation.swift:30:13:30:13 | &... | | stringinterpolation.swift:30:20:30:20 | $interpolation | stringinterpolation.swift:30:20:30:20 | &... | -| stringinterpolation.swift:30:20:30:20 | &... | stringinterpolation.swift:30:20:30:20 | [post] &... | +| stringinterpolation.swift:30:20:30:20 | $interpolation | stringinterpolation.swift:30:20:30:20 | [post] $interpolation | | stringinterpolation.swift:30:20:30:20 | &... | stringinterpolation.swift:30:30:30:30 | $interpolation | -| stringinterpolation.swift:30:20:30:20 | [post] &... | stringinterpolation.swift:30:30:30:30 | $interpolation | +| stringinterpolation.swift:30:20:30:20 | [post] $interpolation | stringinterpolation.swift:30:20:30:20 | &... | | stringinterpolation.swift:30:21:30:21 | [post] p2 | stringinterpolation.swift:31:21:31:21 | p2 | | stringinterpolation.swift:30:21:30:21 | p2 | stringinterpolation.swift:31:21:31:21 | p2 | -| stringinterpolation.swift:30:21:30:24 | .first | stringinterpolation.swift:30:20:30:20 | [post] &... | +| stringinterpolation.swift:30:21:30:24 | .first | stringinterpolation.swift:30:20:30:20 | [post] $interpolation | | stringinterpolation.swift:30:30:30:30 | | stringinterpolation.swift:30:30:30:30 | [post] | -| stringinterpolation.swift:30:30:30:30 | | stringinterpolation.swift:30:30:30:30 | [post] &... | +| stringinterpolation.swift:30:30:30:30 | | stringinterpolation.swift:30:30:30:30 | [post] $interpolation | | stringinterpolation.swift:30:30:30:30 | $interpolation | stringinterpolation.swift:30:30:30:30 | &... | +| stringinterpolation.swift:30:30:30:30 | $interpolation | stringinterpolation.swift:30:30:30:30 | [post] | +| stringinterpolation.swift:30:30:30:30 | $interpolation | stringinterpolation.swift:30:30:30:30 | [post] $interpolation | | stringinterpolation.swift:30:30:30:30 | &... | stringinterpolation.swift:30:12:30:12 | TapExpr | -| stringinterpolation.swift:30:30:30:30 | &... | stringinterpolation.swift:30:30:30:30 | [post] | -| stringinterpolation.swift:30:30:30:30 | &... | stringinterpolation.swift:30:30:30:30 | [post] &... | -| stringinterpolation.swift:30:30:30:30 | [post] &... | stringinterpolation.swift:30:12:30:12 | TapExpr | +| stringinterpolation.swift:30:30:30:30 | [post] $interpolation | stringinterpolation.swift:30:30:30:30 | &... | | stringinterpolation.swift:31:12:31:12 | SSA def($interpolation) | stringinterpolation.swift:31:13:31:13 | $interpolation | | stringinterpolation.swift:31:12:31:12 | TapExpr | stringinterpolation.swift:31:12:31:12 | "..." | | stringinterpolation.swift:31:12:31:12 | pair: | stringinterpolation.swift:31:12:31:12 | [post] pair: | -| stringinterpolation.swift:31:12:31:12 | pair: | stringinterpolation.swift:31:13:31:13 | [post] &... | +| stringinterpolation.swift:31:12:31:12 | pair: | stringinterpolation.swift:31:13:31:13 | [post] $interpolation | +| stringinterpolation.swift:31:13:31:13 | $interpolation | stringinterpolation.swift:31:12:31:12 | [post] pair: | | stringinterpolation.swift:31:13:31:13 | $interpolation | stringinterpolation.swift:31:13:31:13 | &... | -| stringinterpolation.swift:31:13:31:13 | &... | stringinterpolation.swift:31:12:31:12 | [post] pair: | -| stringinterpolation.swift:31:13:31:13 | &... | stringinterpolation.swift:31:13:31:13 | [post] &... | +| stringinterpolation.swift:31:13:31:13 | $interpolation | stringinterpolation.swift:31:13:31:13 | [post] $interpolation | | stringinterpolation.swift:31:13:31:13 | &... | stringinterpolation.swift:31:20:31:20 | $interpolation | -| stringinterpolation.swift:31:13:31:13 | [post] &... | stringinterpolation.swift:31:20:31:20 | $interpolation | +| stringinterpolation.swift:31:13:31:13 | [post] $interpolation | stringinterpolation.swift:31:13:31:13 | &... | | stringinterpolation.swift:31:20:31:20 | $interpolation | stringinterpolation.swift:31:20:31:20 | &... | -| stringinterpolation.swift:31:20:31:20 | &... | stringinterpolation.swift:31:20:31:20 | [post] &... | +| stringinterpolation.swift:31:20:31:20 | $interpolation | stringinterpolation.swift:31:20:31:20 | [post] $interpolation | | stringinterpolation.swift:31:20:31:20 | &... | stringinterpolation.swift:31:31:31:31 | $interpolation | -| stringinterpolation.swift:31:20:31:20 | [post] &... | stringinterpolation.swift:31:31:31:31 | $interpolation | +| stringinterpolation.swift:31:20:31:20 | [post] $interpolation | stringinterpolation.swift:31:20:31:20 | &... | | stringinterpolation.swift:31:21:31:21 | [post] p2 | stringinterpolation.swift:32:21:32:21 | p2 | | stringinterpolation.swift:31:21:31:21 | p2 | stringinterpolation.swift:32:21:32:21 | p2 | -| stringinterpolation.swift:31:21:31:24 | .second | stringinterpolation.swift:31:20:31:20 | [post] &... | +| stringinterpolation.swift:31:21:31:24 | .second | stringinterpolation.swift:31:20:31:20 | [post] $interpolation | | stringinterpolation.swift:31:31:31:31 | | stringinterpolation.swift:31:31:31:31 | [post] | -| stringinterpolation.swift:31:31:31:31 | | stringinterpolation.swift:31:31:31:31 | [post] &... | +| stringinterpolation.swift:31:31:31:31 | | stringinterpolation.swift:31:31:31:31 | [post] $interpolation | | stringinterpolation.swift:31:31:31:31 | $interpolation | stringinterpolation.swift:31:31:31:31 | &... | +| stringinterpolation.swift:31:31:31:31 | $interpolation | stringinterpolation.swift:31:31:31:31 | [post] | +| stringinterpolation.swift:31:31:31:31 | $interpolation | stringinterpolation.swift:31:31:31:31 | [post] $interpolation | | stringinterpolation.swift:31:31:31:31 | &... | stringinterpolation.swift:31:12:31:12 | TapExpr | -| stringinterpolation.swift:31:31:31:31 | &... | stringinterpolation.swift:31:31:31:31 | [post] | -| stringinterpolation.swift:31:31:31:31 | &... | stringinterpolation.swift:31:31:31:31 | [post] &... | -| stringinterpolation.swift:31:31:31:31 | [post] &... | stringinterpolation.swift:31:12:31:12 | TapExpr | +| stringinterpolation.swift:31:31:31:31 | [post] $interpolation | stringinterpolation.swift:31:31:31:31 | &... | | stringinterpolation.swift:32:12:32:12 | SSA def($interpolation) | stringinterpolation.swift:32:13:32:13 | $interpolation | | stringinterpolation.swift:32:12:32:12 | TapExpr | stringinterpolation.swift:32:12:32:12 | "..." | | stringinterpolation.swift:32:12:32:12 | pair: | stringinterpolation.swift:32:12:32:12 | [post] pair: | -| stringinterpolation.swift:32:12:32:12 | pair: | stringinterpolation.swift:32:13:32:13 | [post] &... | +| stringinterpolation.swift:32:12:32:12 | pair: | stringinterpolation.swift:32:13:32:13 | [post] $interpolation | +| stringinterpolation.swift:32:13:32:13 | $interpolation | stringinterpolation.swift:32:12:32:12 | [post] pair: | | stringinterpolation.swift:32:13:32:13 | $interpolation | stringinterpolation.swift:32:13:32:13 | &... | -| stringinterpolation.swift:32:13:32:13 | &... | stringinterpolation.swift:32:12:32:12 | [post] pair: | -| stringinterpolation.swift:32:13:32:13 | &... | stringinterpolation.swift:32:13:32:13 | [post] &... | +| stringinterpolation.swift:32:13:32:13 | $interpolation | stringinterpolation.swift:32:13:32:13 | [post] $interpolation | | stringinterpolation.swift:32:13:32:13 | &... | stringinterpolation.swift:32:20:32:20 | $interpolation | -| stringinterpolation.swift:32:13:32:13 | [post] &... | stringinterpolation.swift:32:20:32:20 | $interpolation | +| stringinterpolation.swift:32:13:32:13 | [post] $interpolation | stringinterpolation.swift:32:13:32:13 | &... | | stringinterpolation.swift:32:20:32:20 | $interpolation | stringinterpolation.swift:32:20:32:20 | &... | -| stringinterpolation.swift:32:20:32:20 | &... | stringinterpolation.swift:32:20:32:20 | [post] &... | -| stringinterpolation.swift:32:20:32:20 | &... | stringinterpolation.swift:32:21:32:21 | [post] p2 | +| stringinterpolation.swift:32:20:32:20 | $interpolation | stringinterpolation.swift:32:20:32:20 | [post] $interpolation | +| stringinterpolation.swift:32:20:32:20 | $interpolation | stringinterpolation.swift:32:21:32:21 | [post] p2 | | stringinterpolation.swift:32:20:32:20 | &... | stringinterpolation.swift:32:24:32:24 | $interpolation | -| stringinterpolation.swift:32:20:32:20 | [post] &... | stringinterpolation.swift:32:24:32:24 | $interpolation | -| stringinterpolation.swift:32:21:32:21 | p2 | stringinterpolation.swift:32:20:32:20 | [post] &... | +| stringinterpolation.swift:32:20:32:20 | [post] $interpolation | stringinterpolation.swift:32:20:32:20 | &... | +| stringinterpolation.swift:32:21:32:21 | p2 | stringinterpolation.swift:32:20:32:20 | [post] $interpolation | | stringinterpolation.swift:32:21:32:21 | p2 | stringinterpolation.swift:32:21:32:21 | [post] p2 | | stringinterpolation.swift:32:24:32:24 | | stringinterpolation.swift:32:24:32:24 | [post] | -| stringinterpolation.swift:32:24:32:24 | | stringinterpolation.swift:32:24:32:24 | [post] &... | +| stringinterpolation.swift:32:24:32:24 | | stringinterpolation.swift:32:24:32:24 | [post] $interpolation | | stringinterpolation.swift:32:24:32:24 | $interpolation | stringinterpolation.swift:32:24:32:24 | &... | +| stringinterpolation.swift:32:24:32:24 | $interpolation | stringinterpolation.swift:32:24:32:24 | [post] | +| stringinterpolation.swift:32:24:32:24 | $interpolation | stringinterpolation.swift:32:24:32:24 | [post] $interpolation | | stringinterpolation.swift:32:24:32:24 | &... | stringinterpolation.swift:32:12:32:12 | TapExpr | -| stringinterpolation.swift:32:24:32:24 | &... | stringinterpolation.swift:32:24:32:24 | [post] | -| stringinterpolation.swift:32:24:32:24 | &... | stringinterpolation.swift:32:24:32:24 | [post] &... | -| stringinterpolation.swift:32:24:32:24 | [post] &... | stringinterpolation.swift:32:12:32:12 | TapExpr | +| stringinterpolation.swift:32:24:32:24 | [post] $interpolation | stringinterpolation.swift:32:24:32:24 | &... | | subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] | | subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] | | subscript.swift:1:7:1:7 | self | subscript.swift:1:7:1:7 | SSA def(self) | diff --git a/swift/ql/test/library-tests/dataflow/taint/core/Taint.expected b/swift/ql/test/library-tests/dataflow/taint/core/Taint.expected index b7c91003b01..3438ed3afeb 100644 --- a/swift/ql/test/library-tests/dataflow/taint/core/Taint.expected +++ b/swift/ql/test/library-tests/dataflow/taint/core/Taint.expected @@ -46,11 +46,11 @@ edges | stringinterpolation.swift:7:6:7:6 | self [second] | file://:0:0:0:0 | self [second] | | stringinterpolation.swift:7:6:7:6 | value | file://:0:0:0:0 | value | | stringinterpolation.swift:11:36:11:44 | pair [first] | stringinterpolation.swift:13:36:13:36 | pair [first] | -| stringinterpolation.swift:13:3:13:3 | [post] &... | stringinterpolation.swift:11:11:14:2 | self[return] | +| stringinterpolation.swift:13:3:13:3 | [post] self | stringinterpolation.swift:11:11:14:2 | self[return] | | stringinterpolation.swift:13:36:13:36 | pair [first] | stringinterpolation.swift:6:6:6:6 | self [first] | | stringinterpolation.swift:13:36:13:36 | pair [first] | stringinterpolation.swift:13:36:13:41 | .first | | stringinterpolation.swift:13:36:13:41 | .first | stringinterpolation.swift:11:11:14:2 | self[return] | -| stringinterpolation.swift:13:36:13:41 | .first | stringinterpolation.swift:13:3:13:3 | [post] &... | +| stringinterpolation.swift:13:36:13:41 | .first | stringinterpolation.swift:13:3:13:3 | [post] self | | stringinterpolation.swift:19:2:19:2 | [post] p1 [first] | stringinterpolation.swift:20:2:20:2 | p1 [first] | | stringinterpolation.swift:19:13:19:20 | call to source() | stringinterpolation.swift:6:6:6:6 | value | | stringinterpolation.swift:19:13:19:20 | call to source() | stringinterpolation.swift:19:2:19:2 | [post] p1 [first] | @@ -59,9 +59,9 @@ edges | stringinterpolation.swift:22:21:22:21 | p1 [first] | stringinterpolation.swift:6:6:6:6 | self [first] | | stringinterpolation.swift:22:21:22:21 | p1 [first] | stringinterpolation.swift:22:21:22:24 | .first | | stringinterpolation.swift:22:21:22:24 | .first | stringinterpolation.swift:22:12:22:12 | "..." | -| stringinterpolation.swift:24:20:24:20 | [post] &... | stringinterpolation.swift:24:12:24:12 | "..." | +| stringinterpolation.swift:24:20:24:20 | [post] $interpolation | stringinterpolation.swift:24:12:24:12 | "..." | | stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:11:36:11:44 | pair [first] | -| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:24:20:24:20 | [post] &... | +| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:24:20:24:20 | [post] $interpolation | | stringinterpolation.swift:28:2:28:2 | [post] p2 [second] | stringinterpolation.swift:31:21:31:21 | p2 [second] | | stringinterpolation.swift:28:14:28:21 | call to source() | stringinterpolation.swift:7:6:7:6 | value | | stringinterpolation.swift:28:14:28:21 | call to source() | stringinterpolation.swift:28:2:28:2 | [post] p2 [second] | @@ -72,7 +72,10 @@ edges | subscript.swift:14:15:14:23 | call to source2() | subscript.swift:14:15:14:26 | ...[...] | | try.swift:9:17:9:24 | call to source() | try.swift:9:13:9:24 | try ... | | try.swift:15:17:15:24 | call to source() | try.swift:15:12:15:24 | try! ... | +| try.swift:18:13:18:25 | try? ... [some:0] | try.swift:18:12:18:27 | ...! | | try.swift:18:18:18:25 | call to source() | try.swift:18:12:18:27 | ...! | +| try.swift:18:18:18:25 | call to source() | try.swift:18:18:18:25 | call to source() [some:0] | +| try.swift:18:18:18:25 | call to source() [some:0] | try.swift:18:13:18:25 | try? ... [some:0] | nodes | file://:0:0:0:0 | .first | semmle.label | .first | | file://:0:0:0:0 | .second | semmle.label | .second | @@ -159,7 +162,7 @@ nodes | stringinterpolation.swift:7:6:7:6 | value | semmle.label | value | | stringinterpolation.swift:11:11:14:2 | self[return] | semmle.label | self[return] | | stringinterpolation.swift:11:36:11:44 | pair [first] | semmle.label | pair [first] | -| stringinterpolation.swift:13:3:13:3 | [post] &... | semmle.label | [post] &... | +| stringinterpolation.swift:13:3:13:3 | [post] self | semmle.label | [post] self | | stringinterpolation.swift:13:36:13:36 | pair [first] | semmle.label | pair [first] | | stringinterpolation.swift:13:36:13:41 | .first | semmle.label | .first | | stringinterpolation.swift:19:2:19:2 | [post] p1 [first] | semmle.label | [post] p1 [first] | @@ -169,7 +172,7 @@ nodes | stringinterpolation.swift:22:21:22:21 | p1 [first] | semmle.label | p1 [first] | | stringinterpolation.swift:22:21:22:24 | .first | semmle.label | .first | | stringinterpolation.swift:24:12:24:12 | "..." | semmle.label | "..." | -| stringinterpolation.swift:24:20:24:20 | [post] &... | semmle.label | [post] &... | +| stringinterpolation.swift:24:20:24:20 | [post] $interpolation | semmle.label | [post] $interpolation | | stringinterpolation.swift:24:21:24:21 | p1 [first] | semmle.label | p1 [first] | | stringinterpolation.swift:28:2:28:2 | [post] p2 [second] | semmle.label | [post] p2 [second] | | stringinterpolation.swift:28:14:28:21 | call to source() | semmle.label | call to source() | @@ -185,13 +188,15 @@ nodes | try.swift:15:12:15:24 | try! ... | semmle.label | try! ... | | try.swift:15:17:15:24 | call to source() | semmle.label | call to source() | | try.swift:18:12:18:27 | ...! | semmle.label | ...! | +| try.swift:18:13:18:25 | try? ... [some:0] | semmle.label | try? ... [some:0] | | try.swift:18:18:18:25 | call to source() | semmle.label | call to source() | +| try.swift:18:18:18:25 | call to source() [some:0] | semmle.label | call to source() [some:0] | subpaths | stringinterpolation.swift:13:36:13:36 | pair [first] | stringinterpolation.swift:6:6:6:6 | self [first] | file://:0:0:0:0 | .first | stringinterpolation.swift:13:36:13:41 | .first | | stringinterpolation.swift:19:13:19:20 | call to source() | stringinterpolation.swift:6:6:6:6 | value | file://:0:0:0:0 | [post] self [first] | stringinterpolation.swift:19:2:19:2 | [post] p1 [first] | | stringinterpolation.swift:22:21:22:21 | p1 [first] | stringinterpolation.swift:6:6:6:6 | self [first] | file://:0:0:0:0 | .first | stringinterpolation.swift:22:21:22:24 | .first | -| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:11:36:11:44 | pair [first] | stringinterpolation.swift:11:11:14:2 | self[return] | stringinterpolation.swift:24:20:24:20 | [post] &... | -| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:11:36:11:44 | pair [first] | stringinterpolation.swift:13:3:13:3 | [post] &... | stringinterpolation.swift:24:20:24:20 | [post] &... | +| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:11:36:11:44 | pair [first] | stringinterpolation.swift:11:11:14:2 | self[return] | stringinterpolation.swift:24:20:24:20 | [post] $interpolation | +| stringinterpolation.swift:24:21:24:21 | p1 [first] | stringinterpolation.swift:11:36:11:44 | pair [first] | stringinterpolation.swift:13:3:13:3 | [post] self | stringinterpolation.swift:24:20:24:20 | [post] $interpolation | | stringinterpolation.swift:28:14:28:21 | call to source() | stringinterpolation.swift:7:6:7:6 | value | file://:0:0:0:0 | [post] self [second] | stringinterpolation.swift:28:2:28:2 | [post] p2 [second] | | stringinterpolation.swift:31:21:31:21 | p2 [second] | stringinterpolation.swift:7:6:7:6 | self [second] | file://:0:0:0:0 | .second | stringinterpolation.swift:31:21:31:24 | .second | #select diff --git a/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift b/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift index 0781c7af58a..9f684139fd3 100644 --- a/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift +++ b/swift/ql/test/library-tests/dataflow/taint/libraries/string.swift @@ -438,7 +438,7 @@ func taintThroughEncodings() { }) tainted.withContiguousStorageIfAvailable({ ptr in - sink(arg: ptr) + sink(arg: ptr) // $ tainted=366 sink(arg: ptr.baseAddress!) // $ MISSING: tainted=366 }) } @@ -599,3 +599,52 @@ func untaintedFields() { sink(arg: String.defaultCStringEncoding) sink(arg: tainted.isContiguousUTF8) } + +func callbackWithCleanPointer(ptr: UnsafeBufferPointer) throws -> Int { + sink(arg: ptr) + + return 0 +} + +func callbackWithTaintedPointer(ptr: UnsafeBufferPointer) throws -> Int { + sink(arg: ptr) // $ tainted=617 + + return source() +} + +func furtherTaintThroughCallbacks() { + let clean = "" + let tainted = source2() + + // return values from the closure (1) + let result1 = clean.withContiguousStorageIfAvailable({ + ptr in + return 0 + }) + sink(arg: result1!) + let result2 = clean.withContiguousStorageIfAvailable({ + ptr in + return source() + }) + sink(arg: result2!) // $ tainted=627 + + // return values from the closure (2) + if let result3 = clean.withContiguousStorageIfAvailable({ + ptr in + return 0 + }) { + sink(arg: result3) + } + if let result4 = clean.withContiguousStorageIfAvailable({ + ptr in + return source() + }) { + sink(arg: result4) // $ tainted=640 + } + + // using a non-closure function + let result5 = try? clean.withContiguousStorageIfAvailable(callbackWithCleanPointer) + sink(arg: result5!) + let result6 = try? tainted.withContiguousStorageIfAvailable(callbackWithTaintedPointer) + sink(arg: result6!) // $ tainted=612 +} diff --git a/swift/ql/test/library-tests/elements/expr/assignment/assignment.expected b/swift/ql/test/library-tests/elements/expr/assignment/assignment.expected index 1fc663c8452..2826d56e8a5 100644 --- a/swift/ql/test/library-tests/elements/expr/assignment/assignment.expected +++ b/swift/ql/test/library-tests/elements/expr/assignment/assignment.expected @@ -1,20 +1,20 @@ | assignment.swift:6:2:6:6 | ... = ... | AssignExpr, Assignment | x | 1 | -| assignment.swift:9:2:9:7 | ... .+=(_:_:) ... | AssignAddExpr, AssignArithmeticOperation, AssignOperation, Assignment | &... | 1 | -| assignment.swift:10:2:10:7 | ... .-=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignSubExpr, Assignment | &... | 1 | -| assignment.swift:11:2:11:7 | ... .*=(_:_:) ... | AssignArithmeticOperation, AssignMulExpr, AssignOperation, Assignment | &... | 1 | -| assignment.swift:12:2:12:7 | ... ./=(_:_:) ... | AssignArithmeticOperation, AssignDivExpr, AssignOperation, Assignment | &... | 1 | -| assignment.swift:13:2:13:7 | ... .%=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignRemExpr, Assignment | &... | 1 | -| assignment.swift:16:2:16:7 | ... .&=(_:_:) ... | AssignAndExpr, AssignBitwiseOperation, AssignOperation, Assignment | &... | 1 | -| assignment.swift:17:2:17:7 | ... .\|=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignOrExpr, Assignment | &... | 1 | -| assignment.swift:18:2:18:7 | ... .^=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignXorExpr, Assignment | &... | 1 | -| assignment.swift:19:2:19:8 | ... .<<=(_:_:) ... | AssignBitwiseOperation, AssignLShiftExpr, AssignOperation, Assignment | &... | 1 | -| assignment.swift:20:2:20:8 | ... .>>=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignRShiftExpr, Assignment | &... | 1 | -| assignment.swift:23:2:23:8 | ... .&*=(_:_:) ... | AssignArithmeticOperation, AssignMulExpr, AssignOperation, Assignment, hasOverflowOperator | &... | 1 | -| assignment.swift:24:2:24:8 | ... .&+=(_:_:) ... | AssignAddExpr, AssignArithmeticOperation, AssignOperation, Assignment, hasOverflowOperator | &... | 1 | -| assignment.swift:25:2:25:8 | ... .&-=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignSubExpr, Assignment, hasOverflowOperator | &... | 1 | -| assignment.swift:26:2:26:9 | ... .&<<=(_:_:) ... | AssignBitwiseOperation, AssignLShiftExpr, AssignOperation, Assignment, hasOverflowOperator | &... | 1 | -| assignment.swift:27:2:27:9 | ... .&>>=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignRShiftExpr, Assignment, hasOverflowOperator | &... | 1 | +| assignment.swift:9:2:9:7 | ... .+=(_:_:) ... | AssignAddExpr, AssignArithmeticOperation, AssignOperation, Assignment | x | 1 | +| assignment.swift:10:2:10:7 | ... .-=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignSubExpr, Assignment | x | 1 | +| assignment.swift:11:2:11:7 | ... .*=(_:_:) ... | AssignArithmeticOperation, AssignMulExpr, AssignOperation, Assignment | x | 1 | +| assignment.swift:12:2:12:7 | ... ./=(_:_:) ... | AssignArithmeticOperation, AssignDivExpr, AssignOperation, Assignment | x | 1 | +| assignment.swift:13:2:13:7 | ... .%=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignRemExpr, Assignment | x | 1 | +| assignment.swift:16:2:16:7 | ... .&=(_:_:) ... | AssignAndExpr, AssignBitwiseOperation, AssignOperation, Assignment | x | 1 | +| assignment.swift:17:2:17:7 | ... .\|=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignOrExpr, Assignment | x | 1 | +| assignment.swift:18:2:18:7 | ... .^=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignXorExpr, Assignment | x | 1 | +| assignment.swift:19:2:19:8 | ... .<<=(_:_:) ... | AssignBitwiseOperation, AssignLShiftExpr, AssignOperation, Assignment | x | 1 | +| assignment.swift:20:2:20:8 | ... .>>=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignRShiftExpr, Assignment | x | 1 | +| assignment.swift:23:2:23:8 | ... .&*=(_:_:) ... | AssignArithmeticOperation, AssignMulExpr, AssignOperation, Assignment, hasOverflowOperator | x | 1 | +| assignment.swift:24:2:24:8 | ... .&+=(_:_:) ... | AssignAddExpr, AssignArithmeticOperation, AssignOperation, Assignment, hasOverflowOperator | x | 1 | +| assignment.swift:25:2:25:8 | ... .&-=(_:_:) ... | AssignArithmeticOperation, AssignOperation, AssignSubExpr, Assignment, hasOverflowOperator | x | 1 | +| assignment.swift:26:2:26:9 | ... .&<<=(_:_:) ... | AssignBitwiseOperation, AssignLShiftExpr, AssignOperation, Assignment, hasOverflowOperator | x | 1 | +| assignment.swift:27:2:27:9 | ... .&>>=(_:_:) ... | AssignBitwiseOperation, AssignOperation, AssignRShiftExpr, Assignment, hasOverflowOperator | x | 1 | | assignment.swift:33:2:33:6 | ... = ... | AssignExpr, Assignment | y | z | -| assignment.swift:34:2:34:8 | ... ..&=(_:_:) ... | AssignOperation, AssignPointwiseAndExpr, Assignment | &... | m | -| assignment.swift:35:2:35:8 | ... ..\|=(_:_:) ... | AssignOperation, AssignPointwiseOrExpr, Assignment | &... | m | -| assignment.swift:36:2:36:8 | ... ..^=(_:_:) ... | AssignOperation, AssignPointwiseXorExpr, Assignment | &... | m | +| assignment.swift:34:2:34:8 | ... ..&=(_:_:) ... | AssignOperation, AssignPointwiseAndExpr, Assignment | m | m | +| assignment.swift:35:2:35:8 | ... ..\|=(_:_:) ... | AssignOperation, AssignPointwiseOrExpr, Assignment | m | m | +| assignment.swift:36:2:36:8 | ... ..^=(_:_:) ... | AssignOperation, AssignPointwiseXorExpr, Assignment | m | m | diff --git a/swift/ql/test/library-tests/regex/regex.swift b/swift/ql/test/library-tests/regex/regex.swift index a53e3d389fd..867d0f613b4 100644 --- a/swift/ql/test/library-tests/regex/regex.swift +++ b/swift/ql/test/library-tests/regex/regex.swift @@ -119,9 +119,9 @@ func myRegexpMethodsTests(b: Bool, str_unknown: String) throws { // --- RangeReplaceableCollection --- var inputVar = input - inputVar.replace(regex, with: "") // $ regex=.* input=&... + inputVar.replace(regex, with: "") // $ regex=.* input=inputVar _ = input.replacing(regex, with: "") // $ regex=.* input=input - inputVar.trimPrefix(regex) // $ regex=.* input=&... + inputVar.trimPrefix(regex) // $ regex=.* input=inputVar // --- StringProtocol --- diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index e7c8443bfa2..a4fc0d17312 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -17,18 +17,32 @@ edges | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:139:48:139:48 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:153:85:153:85 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:154:86:154:86 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | +| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | +| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:140:47:140:47 | remoteURL2 [some:0] | +| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:141:48:141:48 | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | +| UnsafeWebViewFetch.swift:139:48:139:48 | remoteURL [some:0] | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | +| UnsafeWebViewFetch.swift:140:47:140:47 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | +| UnsafeWebViewFetch.swift:141:48:141:48 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:152:15:152:15 | remoteData | | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | +| UnsafeWebViewFetch.swift:153:85:153:85 | remoteURL [some:0] | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | +| UnsafeWebViewFetch.swift:154:86:154:86 | remoteURL [some:0] | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | @@ -38,18 +52,32 @@ edges | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:186:48:186:48 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:200:90:200:90 | remoteURL [some:0] | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:201:91:201:91 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | +| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | +| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:187:47:187:47 | remoteURL2 [some:0] | +| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:188:48:188:48 | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | +| UnsafeWebViewFetch.swift:186:48:186:48 | remoteURL [some:0] | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | +| UnsafeWebViewFetch.swift:187:47:187:47 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | +| UnsafeWebViewFetch.swift:188:48:188:48 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:199:15:199:15 | remoteData | | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | +| UnsafeWebViewFetch.swift:200:90:200:90 | remoteURL [some:0] | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | +| UnsafeWebViewFetch.swift:201:91:201:91 | remoteURL [some:0] | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | nodes @@ -67,22 +95,30 @@ nodes | UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | semmle.label | "..." | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | semmle.label | call to URL.init(string:) | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | semmle.label | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | semmle.label | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | semmle.label | remoteURL | | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:139:48:139:48 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:140:47:140:47 | remoteURL2 [some:0] | semmle.label | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:141:48:141:48 | remoteURL2 [some:0] | semmle.label | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | semmle.label | .utf8 | | UnsafeWebViewFetch.swift:152:15:152:15 | remoteData | semmle.label | remoteData | +| UnsafeWebViewFetch.swift:153:85:153:85 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | semmle.label | remoteData | +| UnsafeWebViewFetch.swift:154:86:154:86 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | @@ -90,22 +126,30 @@ nodes | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | semmle.label | "..." | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | semmle.label | call to URL.init(string:) | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | semmle.label | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | semmle.label | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | semmle.label | remoteURL | | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:186:48:186:48 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:187:47:187:47 | remoteURL2 [some:0] | semmle.label | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:188:48:188:48 | remoteURL2 [some:0] | semmle.label | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | semmle.label | .utf8 | | UnsafeWebViewFetch.swift:199:15:199:15 | remoteData | semmle.label | remoteData | +| UnsafeWebViewFetch.swift:200:90:200:90 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | semmle.label | remoteData | +| UnsafeWebViewFetch.swift:201:91:201:91 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData | diff --git a/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.expected b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.expected new file mode 100644 index 00000000000..2f9dbc0e765 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.expected @@ -0,0 +1,26 @@ +| test.swift:79:26:79:48 | .*?<\\/script> | This regular expression does not match script end tags like . | +| test.swift:86:27:86:49 | .*?<\\/script> | This regular expression does not match script end tags like . | +| test.swift:90:50:90:72 | .*?<\\/script> | This regular expression does not match script end tags like . | +| test.swift:113:26:113:35 | )\|([^\\/\\s>]+)[\\S\\s]*?> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. | +| test.swift:194:50:194:91 | <(?:!--([\\S\|\\s]*?)-->)\|([^\\/\\s>]+)[\\S\\s]*?> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. | +| test.swift:198:27:198:167 | <(?:(?:\\/([^>]+)>)\|(?:!--([\\S\|\\s]*?)-->)\|(?:([^\\/\\s>]+)((?:\\s+[\\w\\-:.]+(?:\\s*=\\s*?(?:(?:"[^"]*")\|(?:'[^']*')\|[^\\s"'\\/>]+))?)*)[\\S\\s]*?(\\/?)>)) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 2 and comments ending with --!> are matched with capture group 3, 4. | +| test.swift:201:50:201:190 | <(?:(?:\\/([^>]+)>)\|(?:!--([\\S\|\\s]*?)-->)\|(?:([^\\/\\s>]+)((?:\\s+[\\w\\-:.]+(?:\\s*=\\s*?(?:(?:"[^"]*")\|(?:'[^']*')\|[^\\s"'\\/>]+))?)*)[\\S\\s]*?(\\/?)>)) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 2 and comments ending with --!> are matched with capture group 3, 4. | +| test.swift:205:51:205:84 | ]*>([\\s\\S]*?)<\\/script> | This regular expression does not match script end tags like . | +| test.swift:209:51:209:104 | (<[a-z\\/!$]("[^"]*"\|'[^']*'\|[^'">])*>\|) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 3 and comments ending with --!> are matched with capture group 1. | +| test.swift:213:51:213:293 | <(?:(?:!--([\\w\\W]*?)-->)\|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)\|(?:!DOCTYPE([\\w\\W]*?)>)\|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)\|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)\|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^"'>]+(?:(?:"[^"]*")\|(?:'[^']*')\|[^>]*))*\|\\/\|\\s+)>)) | This regular expression only parses --> (capture group 1) and not --!> as an HTML comment end tag. | +| test.swift:217:51:217:77 | \|<([^>]*?)> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. | +| test.swift:225:51:225:52 | --> | This regular expression only parses --> and not --!> as a HTML comment end tag. | diff --git a/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref new file mode 100644 index 00000000000..8186dfa236f --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.qlref @@ -0,0 +1 @@ +queries/Security/CWE-116/BadTagFilter.ql \ No newline at end of file diff --git a/swift/ql/test/query-tests/Security/CWE-116/test.swift b/swift/ql/test/query-tests/Security/CWE-116/test.swift new file mode 100644 index 00000000000..7a5412cf868 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-116/test.swift @@ -0,0 +1,231 @@ + +// --- stubs --- + +struct URL { + init?(string: String) {} +} + +extension String { + init(contentsOf: URL) { + let data = "" + self.init(data) + } +} + +struct AnyRegexOutput { +} + +protocol RegexComponent { + associatedtype RegexOutput +} + +struct Regex : RegexComponent { + struct Match { + } + + init(_ pattern: String) throws where Output == AnyRegexOutput { } + + func ignoresCase(_ ignoresCase: Bool = true) -> Regex.RegexOutput> { return self } + func dotMatchesNewlines(_ dotMatchesNewlines: Bool = true) -> Regex.RegexOutput> { return self } + + func firstMatch(in string: String) throws -> Regex.Match? { return nil} + + typealias RegexOutput = Output +} + +extension String : RegexComponent { + typealias Output = Substring + typealias RegexOutput = String.Output +} + +class NSObject { +} + +struct _NSRange { + init(location: Int, length: Int) { } +} + +typealias NSRange = _NSRange + +func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: loc, length: len) } + +class NSTextCheckingResult : NSObject { +} + +class NSRegularExpression : NSObject { + struct Options : OptionSet { + var rawValue: UInt + + static var caseInsensitive: NSRegularExpression.Options { get { return Options(rawValue: 1 << 0) } } + static var dotMatchesLineSeparators: NSRegularExpression.Options { get { return Options(rawValue: 1 << 1) } } + } + + struct MatchingOptions : OptionSet { + var rawValue: UInt + } + + init(pattern: String, options: NSRegularExpression.Options = []) throws { } + + func matches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> [NSTextCheckingResult] { return [] } + func firstMatch(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> NSTextCheckingResult? { return nil } +} + +// --- tests --- + +func myRegexpVariantsTests(myUrl: URL) throws { + let tainted = String(contentsOf: myUrl) // tainted + + // BAD - doesn't match newlines or `` + let re1 = try Regex(#".*?<\/script>"#).ignoresCase(true) + _ = try re1.firstMatch(in: tainted) + + // BAD - doesn't match `` [NOT DETECTED - all regexs with mode flags are currently missed by the query] + let re2a = try Regex(#"(?is).*?<\/script>"#) + _ = try re2a.firstMatch(in: tainted) + // BAD - doesn't match `` + let re2b = try Regex(#".*?<\/script>"#).ignoresCase(true).dotMatchesNewlines(true) + _ = try re2b.firstMatch(in: tainted) + // BAD - doesn't match `` + let options2c: NSRegularExpression.Options = [.caseInsensitive, .dotMatchesLineSeparators] + let ns2c = try NSRegularExpression(pattern: #".*?<\/script>"#, options: options2c) + _ = ns2c.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // GOOD + let re3a = try Regex(#"(?is).*?<\/script[^>]*>"#) + _ = try re3a.firstMatch(in: tainted) + // GOOD + let re3b = try Regex(#".*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true) + _ = try re3b.firstMatch(in: tainted) + // GOOD + let options3b: NSRegularExpression.Options = [.caseInsensitive, .dotMatchesLineSeparators] + let ns3b = try NSRegularExpression(pattern: #".*?<\/script[^>]*>"#, options: options3b) + _ = ns3b.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // GOOD - we don't care regexps that only match comments + let re4 = try Regex(#""#).ignoresCase(true).dotMatchesNewlines(true) + _ = try re4.firstMatch(in: tainted) + + // GOOD + let re5 = try Regex(#")|([^\/\s>]+)[\S\s]*?>"#) + _ = try re16.firstMatch(in: tainted) + // BAD - doesn't match comments with the right capture groups + let ns16 = try NSRegularExpression(pattern: #"<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"#) + _ = ns16.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - capture groups + let re17 = try Regex(#"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#) + _ = try re17.firstMatch(in: tainted) + // BAD - capture groups + let ns17 = try NSRegularExpression(pattern: #"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#, options: .caseInsensitive) + _ = ns17.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - too strict matching on the end tag + let ns2_1 = try NSRegularExpression(pattern: #"]*>([\s\S]*?)<\/script>"#, options: .caseInsensitive) + _ = ns2_1.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - capture groups + let ns2_2 = try NSRegularExpression(pattern: #"(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)"#, options: .caseInsensitive) + _ = ns2_2.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - capture groups + let ns2_3 = try NSRegularExpression(pattern: #"<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"#) + _ = ns2_3.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - capture groups + let ns2_4 = try NSRegularExpression(pattern: #"|<([^>]*?)>"#) + _ = ns2_4.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // GOOD - it's used with the ignorecase flag + let ns2_5 = try NSRegularExpression(pattern: #"]*)>([\S\s]*?)<\/script([^>]*)>"#, options: .caseInsensitive) + _ = ns2_5.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // BAD - doesn't match --!> + let ns2_6 = try NSRegularExpression(pattern: #"-->"#) + _ = ns2_6.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count)) + + // GOOD + let ns2_7 = try NSRegularExpression(pattern: #"^>|^->||--!>|