Skip to content

C++: Model taint flow through pointer-returning methods using MaD summaries #21623

@hello123leo

Description

@hello123leo

Hi, i am trying to use MaD summary rules to model taint tracking through a protobuf-style API pattern where a container method returns a pointer to an internal element, and taint is written through that pointer.

c++ code(simplified protobuf-like pattern):

#include <iostream>
#include <string>

class Person {
public:
    void set_name(const std::string& name) { name_ = name; }
    const std::string& name() const { return name_; }
    std::string DebugString() const {
        return "Person { name: \"" + name_ + "\" }";
    }
private:
    std::string name_;
};

class AddressBook {
public:
    AddressBook() : count_(0) {}
    Person* add_people() {
        people_[count_] = new Person();
        return people_[count_++];
    }
    std::string DebugString() const {
        std::string result = "AddressBook { ";
        for (int i = 0; i < count_; i++) {
            result += people_[i]->DebugString() + " ";
        }
        result += "}";
        return result;
    }
    ~AddressBook() {
        for (int i = 0; i < count_; i++) delete people_[i];
    }
private:
    Person* people_[10];
    int count_;
};

std::string source_func() {
    return "source";
}

void sink_func(const AddressBook& book) {
    std::cout << book.DebugString() << std::endl;
}

int main() {
    std::string name = source_func();
    AddressBook address_book;
    Person* person1 = address_book.add_people();
    person1->set_name(name);
    sink_func(address_book);
    return 0;
}

query:

import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import Flow::PathGraph

module Config implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node node) {
    exists(FunctionCall call |
      call.getTarget().hasName("source_func") and
      node.asExpr() = call
    )
  }

  predicate isSink(DataFlow::Node node) {
    exists(FunctionCall call |
      call.getTarget().hasName("sink_func") and
      node.asIndirectExpr() = call.getArgument(0)
    )
  }

  predicate allowImplicitRead(DataFlow::Node n, DataFlow::ContentSet cs) {
    isSink(n)
  }
}

module Flow = TaintTracking::Global<Config>;

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink,
  "$@ to sink_func argument",
  source.getNode(), "source_func()"

MaD:

extensions:
  - addsTo:
      pack: codeql/cpp-all
      extensible: summaryModel
    data:
      - ["", "Person", true, "set_name", "", "", "Argument[*0]", "Argument[-1].Field[name_]", "value", "manual"]
      - ["", "AddressBook", true, "add_people", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]

I need taint to propagate to address_book but my query produces no results, how should i adjust my MaD rules so that address_book is recognized as tainted?

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions