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
14 changes: 14 additions & 0 deletions cpp/ql/lib/ext/std.format.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*1]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*2]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*3]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*4]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*5]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*6]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*7]", "ReturnValue", "taint", "manual"]
- ["std", "", False, "format<Args>", "(format_string,Args &&)", "", "Argument[*8]", "ReturnValue", "taint", "manual"]
93 changes: 41 additions & 52 deletions cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -435,12 +435,17 @@ private predicate elementSpec(
}

/** Gets the fully templated version of `f`. */
private Function getFullyTemplatedMemberFunction(Function f) {
private Function getFullyTemplatedFunction(Function f) {
not f.isFromUninstantiatedTemplate(_) and
exists(Class c, Class templateClass, int i |
c.isConstructedFrom(templateClass) and
f = c.getAMember(i) and
result = templateClass.getCanonicalMember(i)
(
exists(Class c, Class templateClass, int i |
c.isConstructedFrom(templateClass) and
f = c.getAMember(i) and
result = templateClass.getCanonicalMember(i)
)
or
not exists(f.getDeclaringType()) and
f.isConstructedFrom(result)
)
}

Expand All @@ -464,14 +469,14 @@ string getParameterTypeWithoutTemplateArguments(Function f, int n) {
*/
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
exists(Function templateFunction |
templateFunction = getFullyTemplatedMemberFunction(f) and
templateFunction = getFullyTemplatedFunction(f) and
remaining = templateFunction.getNumberOfTemplateArguments() and
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
)
or
exists(string mid, TemplateParameter tp, Function templateFunction |
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
templateFunction = getFullyTemplatedMemberFunction(f) and
templateFunction = getFullyTemplatedFunction(f) and
tp = templateFunction.getTemplateArgument(remaining) and
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
)
Expand All @@ -482,12 +487,18 @@ private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remain
* with `class:N` (where `N` is the index of the template).
*/
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
// If there is a declaring type then we start by expanding the function templates
exists(Class template |
f.getDeclaringType().isConstructedFrom(template) and
remaining = template.getNumberOfTemplateArguments() and
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
)
or
// If there is no declaring type we're done after expanding the function templates
not exists(f.getDeclaringType()) and
remaining = 0 and
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
or
exists(string mid, TemplateParameter tp, Class template |
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
f.getDeclaringType().isConstructedFrom(template) and
Expand Down Expand Up @@ -570,38 +581,6 @@ private string getSignatureWithoutFunctionTemplateNames(
)
}

private string paramsStringPart(Function c, int i) {
not c.isFromUninstantiatedTemplate(_) and
(
i = -1 and result = "(" and exists(c)
or
exists(int n, string p | getParameterTypeName(c, n) = p |
i = 2 * n and result = p
or
i = 2 * n - 1 and result = "," and n != 0
)
or
i = 2 * c.getNumberOfParameters() and result = ")"
)
}

/**
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
*
* Returns the empty string if the callable has no parameters.
* Parameter types are represented by their type erasure.
*/
cached
private string paramsString(Function c) {
result = concat(int i | | paramsStringPart(c, i) order by i)
}

bindingset[func]
private predicate matchesSignature(Function func, string signature) {
signature = "" or
paramsString(func) = signature
}

/**
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
* - `typeArgs` represents the named template parameters supplied to `type`, and
Expand Down Expand Up @@ -750,17 +729,17 @@ private predicate elementSpecWithArguments0(

/**
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
* `method`'s signature matches `signature`.
* `func`'s signature matches `signature`.
*
* `signature` may contain template parameter names that are bound by `type` and `name`.
*/
pragma[nomagic]
private predicate elementSpecMatchesSignature(
Function method, string namespace, string type, boolean subtypes, string name, string signature
Function func, string namespace, string type, boolean subtypes, string name, string signature
) {
elementSpec(namespace, pragma[only_bind_into](type), subtypes, pragma[only_bind_into](name),
pragma[only_bind_into](signature), _) and
signatureMatches(method, signature, type, name, 0)
signatureMatches(func, signature, type, name, 0)
}

/**
Expand All @@ -776,13 +755,22 @@ private predicate hasClassAndName(Class classWithMethod, Function method, string
)
}

bindingset[name]
pragma[inline_late]
private predicate funcHasQualifiedName(Function func, string namespace, string name) {
exists(string nameWithoutArgs |
parseAngles(name, nameWithoutArgs, _, "") and
func.hasQualifiedName(namespace, nameWithoutArgs)
)
}

/**
* Holds if `namedClass` is in namespace `namespace` and has
* name `type` (excluding any template parameters).
*/
bindingset[type, namespace]
pragma[inline_late]
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
private predicate classHasQualifiedName(Class namedClass, string namespace, string type) {
exists(string typeWithoutArgs |
parseAngles(type, typeWithoutArgs, _, "") and
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
Expand All @@ -804,15 +792,16 @@ private Element interpretElement0(
string namespace, string type, boolean subtypes, string name, string signature
) {
(
elementSpec(namespace, type, subtypes, name, signature, _) and
// Non-member functions
exists(Function func |
func.hasQualifiedName(namespace, name) and
type = "" and
matchesSignature(func, signature) and
subtypes = false and
not exists(func.getDeclaringType()) and
result = func
elementSpec(namespace, type, subtypes, name, signature, _) and
subtypes = false and
type = "" and
(
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature)
or
signature = "" and
elementSpec(namespace, type, subtypes, name, "", _) and
funcHasQualifiedName(result, namespace, name)
)
or
// Member functions
Expand All @@ -825,7 +814,7 @@ private Element interpretElement0(
elementSpec(namespace, type, subtypes, name, "", _) and
hasClassAndName(classWithMethod, result, name)
) and
hasQualifiedName(namedClass, namespace, type) and
classHasQualifiedName(namedClass, namespace, type) and
(
// member declared in the named type or a subtype of it
subtypes = true and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
| Dubious signature "(const_iterator,size_type,const T &)" in summary model. |
| Dubious signature "(deque &&)" in summary model. |
| Dubious signature "(deque &&,const Allocator &)" in summary model. |
| Dubious signature "(format_string,Args &&)" in summary model. |
| Dubious signature "(forward_list &&)" in summary model. |
| Dubious signature "(forward_list &&,const Allocator &)" in summary model. |
| Dubious signature "(list &&)" in summary model. |
Expand Down
10 changes: 9 additions & 1 deletion cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

#include "stl.h"
typedef unsigned long size_t;
typedef struct {} FILE;

Expand Down Expand Up @@ -157,3 +157,11 @@ void test2()
sink(s[strlen(s) - 1]); // $ ast,ir
sink(ws + (wcslen(ws) / 2)); // $ ast,ir
}

void test_format() {
auto s = std::format("{}", string::source());
sink(s); // $ ir MISSING: ast

auto s2 = std::format(string::source());
sink(s2); // $ ir MISSING: ast
}
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ WARNING: module 'TaintTracking' has been deprecated and may be removed in future
| format.cpp:158:13:158:18 | call to wcslen | format.cpp:158:13:158:26 | ... / ... | TAINT |
| format.cpp:158:13:158:26 | ... / ... | format.cpp:158:7:158:27 | ... + ... | TAINT |
| format.cpp:158:26:158:26 | 2 | format.cpp:158:13:158:26 | ... / ... | TAINT |
| format.cpp:162:12:162:22 | call to format | format.cpp:163:8:163:8 | s | |
| format.cpp:162:24:162:27 | {} | format.cpp:162:24:162:27 | call to basic_format_string | TAINT |
| format.cpp:165:13:165:23 | call to format | format.cpp:166:8:166:9 | s2 | |
| format.cpp:165:25:165:38 | call to source | format.cpp:165:25:165:40 | call to basic_format_string | TAINT |
| map.cpp:21:28:21:28 | call to pair | map.cpp:23:2:23:2 | a | |
| map.cpp:21:28:21:28 | call to pair | map.cpp:24:7:24:7 | a | |
| map.cpp:21:28:21:28 | call to pair | map.cpp:25:7:25:7 | a | |
Expand Down
35 changes: 35 additions & 0 deletions cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,3 +642,38 @@ namespace std {
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
};
}

// --- string view ---

namespace std {
template<class CharT, class Traits = char_traits<CharT>>
class basic_string_view {
public:
using size_type = size_t;

basic_string_view() noexcept;
basic_string_view(const basic_string_view&) noexcept;
basic_string_view(const CharT*, size_type);
basic_string_view(const CharT*);
template<class It, class End> basic_string_view(It, End);
template<class R> explicit basic_string_view(R&&);
basic_string_view& operator=(const basic_string_view&) noexcept;
};

using string_view = basic_string_view<char>;
}

// --- format ---
namespace std {
template<class CharT /* class... Args */>
struct basic_format_string {
public:
template<class T> basic_format_string(const T&);

basic_string_view<CharT> get() const noexcept;
};

using format_string = basic_format_string<char>; // simplified from `char, std::type_identity_t<Args>...`

template<class... Args> string format( format_string fmt, Args&&... args );
}
Loading