Skip to content

Unified: adopt dynamic-style MaD and replace LegacyMaD - #22936

Closed
asgerf wants to merge 2 commits into
mainfrom
asgerf-dynamic-mad-for-unified
Closed

asgerf wants to merge 2 commits into
mainfrom
asgerf-dynamic-mad-for-unified

Conversation

@asgerf

@asgerf asgerf commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

The unified analysis should work without dependencies being present in the database. The MaD models inherited from the old Swift analysis assume type information is available, which requires dependencies. This PR switches unified to the dynamic-style MaD format (as used for JavaScript/Python/Ruby), where models are (type, path) pairs and typeModel rows describe how instances of a type can be obtained.

Approach

  • Interpreter (internal/mad/DynamicMaD.qll, extensibles in DynamicMaDExtensions.qll): a standalone, unified-specific evaluator of sourceModel/sinkModel/summaryModel/typeModel rows. Type strings are module-qualified: Foundation (module), Foundation.FileManager (instances), Foundation.FileManager.Type (metatype).
    • Instances of a type are found from module-member references, type annotations, casts, declared types of user properties/functions, init calls, typeModel rows, and user subclasses/conformers (including Parameter[..] on overriding methods and implicit self member accesses).
    • Tokens: Member[name] (base name only; initializers are Member[init] on the metatype), WithNamedArgument[l] / WithoutNamedArgument[l] to disambiguate overloads by argument labels, Argument[..], Parameter[..], ReturnValue. Optionals are transparent. Only local flow is followed between steps.
    • Models::isSource/isSink keep their existing signatures.
  • Flow summaries (FlowSummary.qll): summarized callables are keyed on (type, path); type-kind summaries are applied as type steps.
  • Type-model generator (internal/mad/TypeModelGenerator.qll, ql/src/utils/modelgenerator/GenerateTypeModels.ql, scripts/generate-type-models.sh): instantiates codeql.mad.dynamic.GraphExport over name-binding nodes from .swiftinterface files (plus nodes for callables, function types and undeclared types). Only types used by existing source/sink/summary models are exported. SwiftInterfaceModule in NameBindingPluginSwift.qll makes each interface file a module scope. Output from 10 local SDK modules is committed in ext/generated/swift-type-models.model.yml.
  • Converted models: the legacy rows were converted to ext/swift-<module>.model.yml, with minimal label filters to separate overloads. LegacyMaD.qll and legacy-swift.model.yml are deleted.
  • Extractor: computed properties now keep their declaration modifiers (public, static), and functions/initializers keep their attributes (@_spi, @available), which the generator needs. Two corpus cases added.
  • Tests: new dynamic-mad (interpreter) and type-model-generator tests; library stubs in existing tests moved into per-module .swiftinterface files; a test checks the shipped models have no invalid access paths.

Things worth reviewing

  • Classes imported from Objective-C (FileManager, NSString, UIKit classes) do not appear in SDK .swiftinterface files, so their type models cannot be generated. A few key ones are hand-written in ext/swift-modules.model.yml, along with module re-exports.
  • A module is considered available if it is imported anywhere in the database (plus Swift and declared re-exports).
  • Overloads that take only positional arguments get no label filters.
  • Known gaps: trailing closures do not match Argument[label:], property types reached through another value's member are not resolved, and generic arguments are not tracked.

Results

Security query tests lose no expected alerts, and about 15 previously MISSING results are now found (e.g. Process.executableURL, Realm.Configuration.fileURL, textField.text). On Alamofire, sources are unchanged (42) and sinks go 131 -> 135. On Vapor, sinks go 83 -> 77, mostly dropping name-only false matches such as SQLite/GRDB insert.

asgerf and others added 2 commits October 29, 2026 15:08
- Add DynamicMaD.qll, an interpreter for sourceModel/sinkModel/summaryModel/typeModel
  rows identified by (type, path) pairs that does not require type information.
- Rework FlowSummary.qll to key summarized callables on (type, path).
- Add a type-model generator (GraphExport over name-binding nodes) for
  .swiftinterface files, with GenerateTypeModels.ql and generate-type-models.sh.
- Treat .swiftinterface files as module scopes in name binding.
- Preserve declaration modifiers on computed properties and attributes on
  functions/initializers in the extractor.
- Convert the legacy Swift models and delete LegacyMaD.qll.
- Move library stubs in tests into .swiftinterface files.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Only treat calls to a user subclass (or its init) as instances, not other static calls.
- Evaluate the first access-path token per (type, token) instead of per model row.
- Handle module-qualified type references in the type-model generator.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@asgerf
asgerf force-pushed the asgerf-dynamic-mad-for-unified branch from 2ca882a to 575c105 Compare October 29, 2026 14:10
* `kind` is either `value`, `taint`, or `type`. A summary of kind `type` indicates that the value at
* `output` (relative to `(type, path)`) has the same type as the value at `input`.
*/
extensible predicate summaryModel(

/** Holds if there is an edge `pred -> succ` labeled with `path`. */
predicate graphEdge(Node pred, string path, Node succ) {
exists(Member member, string name |

bindingset[host]
predicate hasTypeSummary(Node host, string path) {
exists(Member member, string name |
@asgerf asgerf closed this Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants