Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CodeQL_Queries/cpp/Chrome/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Code QL library for Chrome

This repository contains various [CodeQL](https://codeql.com) libraries for [Chromium](https://chromium.googlesource.com/chromium/src.git). In order to use it, follow the instructions to install the [CodeQL plugin in eclipse](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/installation.html) and then import this repository as an eclipse project.

## Building snapshot

As the Chromium source code is huge. In order to use CodeQL with it, you are likely to need to build your own Chromium snapshot and restrict it to a specific target, instead of building and running CodeQL on the whole of Chromium. This can be achieved by a two stage build. For example, if the directory of interest is `content/browser` (e.g. when researching Chromium IPC), then first build the entire Chromium. Next, remove all the build artifacts (*.o, *.so, *.a) in the `src/out/<target>/obj/content/browser/` directory (including subdirectories) and rebuild with CodeQL, following the instructions [here](https://help.semmle.com/codeql/codeql-cli.html). This should result in a usable size snapshot. Note, however, that building with CodeQL CLI is a memory intensive process and even a restricted snapshot would take up a huge amount of RAM (e.g. `content/browser` requires at least 50-60GB of RAM to build) and I'd recommended building it in a VM in the cloud.

## Overview of various libraries

The libraries in this repository are organized as follows:

### `commons.qll`:

Mostly contain general enchancement to the standard QL library and some Chromium specific, but still general material. For example, because the operators `->` and `=` are often overloaded in Chrome, this somehow upsets the usual `getQualifier` and `Assignment` in QL, so two general methods, `getQualifier` and `GeneralAssignment` are implemented to take these into account. Another useful predicates are `constructionCall` and `polyConstructionCall`, which identify all the constructor calls, as well as constructions via `make_unique` and `MakeRefCounted` (the former works fine in code search, but not the later, but neither will work on vanilla QL (you just ended inside the standard library))

### `collections.qll`:

Generally deals with `std::map`, `std::vector` etc. I use some heuristics there because there are just too many different types of containers and it is likely to miss out some if I add them manually. The library provides methods that get the component types of a container and also function calls that set/reset components in a container. A set/reset method call (e.g. push_back/erase/clear etc.) are useful as they are needed to identify when raw pointers might get remove and when managed pointers may get reset (which will cause the underlying object to be deleted and may cause a UaF somewhere else) Many bugs are actually related to pointers/managed pointers stored in containers.

### `callbacks.qll`:

The is a very useful library. It provides utilities to track the pointer types that are stored inside a callback, both retained and unretained. It uses dataflow in `callback_tracking.qll` to do this and can track through callback fields and multiple callbacks, e.g.

```c
A* a;
...
assignCallback(base::BindOnce(&foo, Unretained(a)));


void assignCallback(Callback callback) {
callback_ = std::move(callback);
}

...
void bar() {
x = base::BindOnce(&foo2, std::move(callback_)); //<-- x unretains type A via the assignCallback call.
}
```

I normally use this as a look up during investigation to see whether a particular callback is dangerous or not.

### `callback_tracking.qll`

Only used by `callbacks.qll` to track the types stored in callbacks.

### `bindings.qll`

Use for modelling mojom interface.

## `pointers`:

QL libraries for managed and raw pointers.

## `object_lifetime`:

### `lifetime_management.qll`:

QL library that models various clean up logic in Chrome. I haven't got round to implement too much code there yet.

`obj_lifetime.qll`:

Mostly contain classes that are long living, e.g. BrowserContext, Singleton and the objects that they manage. Mostly use to exclude raw pointer results as these are not likely to be destroyed.
99 changes: 99 additions & 0 deletions CodeQL_Queries/cpp/Chrome/bindings.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import cpp
import common

/**
* Library for mojo bindings.
*/

class StrongBinding extends ClassTemplateInstantiation {
StrongBinding() {
getName().matches("StrongBinding%")
}

Type getBindingType() {
result = this.getTemplateArgument(0).stripType()
}
}

class Binding extends ClassTemplateInstantiation {
Binding() {
getName().matches("Binding<%")
}

Type getBindingType() {
result = this.getTemplateArgument(0).stripType()
}
}


class MojoReceiver extends ClassTemplateInstantiation {
MojoReceiver() {
getQualifiedName().matches("%mojo::Receiver<%")
}

Type getBindingType() {
result = this.getTemplateArgument(0).stripType()
}
}

class InterfaceBinding extends Class {
FunctionCall addBinding;

InterfaceBinding() {
addBinding.getTarget().hasName("AddBinding") and
this = generalStripType(addBinding.getArgument(0).getAChild*().getType())
}

FunctionCall getABinding() {
result = addBinding
}
}

class InterfacePtr extends ClassTemplateInstantiation {
InterfacePtr() {
stripType().getName().matches("InterfacePtr<%") or
stripType().getName().matches("InterfacePtrInfo<%")
}

Type getInterfaceType() {
result = getTemplateArgument(0)
}

Type getInterfacePtrType() {
exists(string s | s = getInterfaceType().getName() + "Ptr" and
result.getName() = s
)
}
}

class SetConnectionErrorHandler extends Function {
SetConnectionErrorHandler() {
getName() = "set_connection_error_handler" or
getName() = "set_connection_error_with_reason_handler"
}
}

/**
* `StructPtr` usually use for transporting data in IPC.
*/
class StructPtr extends Class {
StructPtr() {
getName().matches("StructPtr<%") or
getName().matches("InlinedStructPtr<%")
}

Type getStructType() {
result = getTemplateArgument(0)
}
}

Type stripStructPtrType(Type c) {
(
c.getName().matches("vector<%") and
result = stripStructPtrType(c.(Class).getTemplateArgument(0))
) or
exists(StructPtr t | t = c.stripType() and
result = t.getStructType().stripType()
) or
result = c.stripType()
}
137 changes: 137 additions & 0 deletions CodeQL_Queries/cpp/Chrome/callback_tracking.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import cpp
import common
import bindings
import field
import callbacks

/**
* Dataflow library for tracking unretained or retained types in a callback.
*/

/**
* An assignment to a callback field.
*/
predicate isCallbackFieldSink(DataFlow::Node sink) {
exists(Field f | sink.asExpr() = generalAssignValue(f))
}

/**
* Running of a callback.
*/
predicate runCallbackSink(DataFlow::Node sink) {
exists(RunCallback cb | cb.getCallback() = sink.asExpr())
}

/**
* An expression that posts a callback to a task runner.
*/
predicate postTaskSink(DataFlow::Node sink) {
exists(FunctionCall postTask | postTask.getTarget().getName().matches("PostTask%") or
postTask.getTarget().getName() = "PostDelayedTask" |
postTask.getAnArgument() = sink.asExpr()
)
}

/**
* Callback gets passed inside an interface pointer function. The idea is that such a
* callback may then be called from the renderer. (A bit like
* https://bugs.chromium.org/p/project-zero/issues/detail?id=1755
* )
*/
predicate interfacePtrCallSink(DataFlow::Node sink) {
exists(FunctionCall mojom, InterfacePtr iPtr, Function interfaceFunc |
overrides*(mojom.getTarget(), interfaceFunc) and
interfaceFunc.getDeclaringType() = iPtr.getInterfaceType() and
sink.asExpr() = mojom.getAnArgument()
) or
exists(BindCall bc, InterfacePtr iPtr, Function interfaceFunc |
overrides*(bc.getFunction(), interfaceFunc) and interfaceFunc.getDeclaringType() = iPtr.getInterfaceType() and
sink.asExpr() = bc.getAnArgument() and
sink.asExpr() != bc.getArgument(0)
)
}

/**
* Callback that then binds to another callback as an argument.
*/
predicate callbackArgSink(DataFlow::Node sink) {
exists(GeneralCallback cb | cb.getACallbackArg() = sink.asExpr())
}

class CallbackConfig extends DataFlow::Configuration {
CallbackConfig() {
this = "callbackconfig"
}

override predicate isSource(DataFlow::Node source) {
(
exists(GeneralCallback fc |
source.asExpr() = fc
)
or
exists(CallbackField f | source.asExpr() = f.getAnAccess())
)
and
not source.asExpr().getFile().getBaseName() = "bind.h" and
not source.asExpr().getFile().getBaseName() = "callback_helpers.h"// and
}

override predicate isSink(DataFlow::Node sink) {
(
isCallbackFieldSink(sink)
or
runCallbackSink(sink)
or
postTaskSink(sink)
or
interfacePtrCallSink(sink)
or
callbackArgSink(sink)
) and
(
//Exclude sinks that are in uninteresting files.
not sink.asExpr().getFile().getBaseName() = "bind_internal.h" and
not sink.asExpr().getFile().getBaseName() = "tuple" and
not sink.asExpr().getFile().getBaseName() = "memory" and
not sink.asExpr().getFile().getAbsolutePath().matches("%/libc++/%") and
not sink.asExpr().getFile().getBaseName() = "bind.h" and
not sink.asExpr().getFile().getBaseName() = "binding_state.h" and
not sink.asExpr().getFile().getBaseName() = "interface_endpoint_client.h" and
not sink.asExpr().getFile().getBaseName() = "associated_interface_registry.h" and
not sink.asExpr().getFile().getBaseName() = "callback_helpers.h" and
not sink.asExpr().getFile().getBaseName() = "callback_list.h" and
sink.asExpr().fromSource()
)
}

override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
callbackStep(node1, node2) or
collectionsEdge(node1, node2) or
getEdge(node1, node2) or
generalAssignEdge(node1, node2) or
exists(Parameter p | p = node1.asParameter() and
node2.asExpr() = p.getAnAccess()
) or
copyConstructorEdge(node1, node2)
or
pointerTransferEdge(node1, node2)
or
adaptCallbackEdge(node1, node2)
or
callbackWrapperEdge(node1, node2)
or
callbackToBindEdge(node1, node2)
or
polymorphicCallEdge(node1, node2)
or
passEdge(node1, node2)
or
ownedEdge(node1, node2)
or
retainedRefEdge(node1, node2)
or
unretainedEdge(node1, node2)
or
forRangeEdge(node1, node2)
}
}
Loading