Skip to content
Open
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
4 changes: 4 additions & 0 deletions cpp/ql/lib/change-notes/2026-09-15-bdlb-stringref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added models for BDE character-view construction and access, and `bdlb::StringRefUtil` trimming, substring, and search operations.
17 changes: 17 additions & 0 deletions cpp/ql/lib/ext/bdlb.stringref.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# BDE character views and substring utilities.
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data:
- ["BloombergLP::bslstl", "StringRefImp", False, "data", "", "", "Argument[-1].Element[]", "ReturnValue[*]", "value", "manual"]
- ["BloombergLP::bslstl", "StringRefImp", False, "begin", "", "", "Argument[-1].Element[]", "ReturnValue[*]", "value", "manual"]
- ["BloombergLP::bslstl", "StringRefImp", False, "operator[]", "", "", "Argument[-1].Element[]", "ReturnValue[*]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "trim", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "ltrim", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "rtrim", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "substr", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "strstr", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "strrstr", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "strstrCaseless", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
- ["BloombergLP::bdlb", "StringRefUtil", False, "strrstrCaseless", "", "", "Argument[*0].Element[]", "ReturnValue.Element[]", "value", "manual"]
2 changes: 2 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/models/Models.qll
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ private import implementations.CAtlTemporaryFile
private import implementations.CRegKey
private import implementations.WinHttp
private import implementations.Http

private import implementations.BdlbStringRef
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/** Models construction of BDE character views. */

private import cpp
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl::Public

/** Only pointer-based constructors read character data from their first argument. */
private class CharacterViewConstructor extends SummarizedCallable {
CharacterViewConstructor() {
this instanceof Constructor and
this.getDeclaringType().hasQualifiedName("BloombergLP::bslstl", "StringRefImp") and
this.getParameter(0).getUnspecifiedType() instanceof PointerType
}

override predicate propagatesFlow(
string input, string output, boolean preservesValue, Provenance provenance, boolean isExact,
string model
) {
input = "Argument[*0]" and
output = "Argument[-1].Element[]" and
preservesValue = true and
provenance = "manual" and
isExact = true and
model = ""
}
}
Empty file.
19 changes: 19 additions & 0 deletions cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import cpp
import utils.test.dataflow.FlowTestCommon
import semmle.code.cpp.ir.dataflow.TaintTracking

module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
node.asExpr().(FunctionCall).getTarget().hasName("source")
}

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

module Flow = TaintTracking::Global<Config>;

import MakeTest<IRFlowTest<Flow>>
91 changes: 91 additions & 0 deletions cpp/ql/test/library-tests/dataflow/bdlb-stringref/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Reduced declarations from bslstl_stringref.h and bdlb_stringrefutil.h.
namespace bsl {
template<bool, class T> struct enable_if {};
template<class T> struct enable_if<true, T> { typedef T type; };
template<class T> struct is_integral { enum { value = false }; };
template<> struct is_integral<int> { enum { value = true }; };
}
namespace BloombergLP {
namespace bslmf { struct Nil {}; }
namespace bslstl {
template<class C> class StringRefImp {
public:
typedef unsigned long size_type;
typedef const C *const_iterator;
StringRefImp();
StringRefImp(const C *);
StringRefImp(const C *, size_type);
// Integral overload: the upstream enable_if resolves to bslmf::Nil.
template<class I> StringRefImp(const C *, I,
typename bsl::enable_if<bsl::is_integral<I>::value, bslmf::Nil>::type = bslmf::Nil());
StringRefImp(const_iterator, const_iterator);
const C *data() const;
const_iterator begin() const;
const C& operator[](size_type) const;
size_type length() const;
};
typedef StringRefImp<char> StringRef;
}
namespace bdlb {
struct StringRefUtil {
typedef unsigned long size_type;
static bslstl::StringRef trim(const bslstl::StringRef&);
static bslstl::StringRef ltrim(const bslstl::StringRef&);
static bslstl::StringRef rtrim(const bslstl::StringRef&);
static bslstl::StringRef substr(const bslstl::StringRef&, size_type = 0, size_type = -1);
static bslstl::StringRef strstr(const bslstl::StringRef&, const bslstl::StringRef&);
static bslstl::StringRef strrstr(const bslstl::StringRef&, const bslstl::StringRef&);
static bslstl::StringRef strstrCaseless(const bslstl::StringRef&, const bslstl::StringRef&);
static bslstl::StringRef strrstrCaseless(const bslstl::StringRef&, const bslstl::StringRef&);
};
}
}
using BloombergLP::bslstl::StringRef;
using BloombergLP::bdlb::StringRefUtil;
int source();
void sink(int);

void views() {
char buffer[] = {static_cast<char>(source()), 0};
StringRef view(buffer);
sink(view.data()[0]); // $ ir
sink(view[0]); // $ ir
sink(*view.begin()); // $ ir
StringRef copy(view);
sink(copy[0]); // $ ir
StringRef sized(buffer, static_cast<unsigned long>(1));
sink(sized[0]); // $ ir
StringRef range(buffer, buffer + 1);
sink(range[0]); // $ ir
StringRef integral(buffer, 1);
sink(integral[0]); // $ ir
sink(view.length());
sink(StringRefUtil::trim(view)[0]); // $ ir
sink(StringRefUtil::ltrim(view)[0]); // $ ir
sink(StringRefUtil::rtrim(view)[0]); // $ ir
sink(StringRefUtil::substr(view)[0]); // $ ir
sink(StringRefUtil::strstr(view, "x")[0]); // $ ir
sink(StringRefUtil::strrstr(view, "x")[0]); // $ ir
sink(StringRefUtil::strstrCaseless(view, "x")[0]); // $ ir
sink(StringRefUtil::strrstrCaseless(view, "x")[0]); // $ ir
}

void metadataAndPatterns() {
unsigned long length = source();
StringRef clean("clean", length);
sink(clean[0]);
sink(StringRefUtil::substr(clean, length, length)[0]);
char buffer[] = {static_cast<char>(source()), 0};
StringRef pattern(buffer);
sink(StringRefUtil::strstr(clean, pattern)[0]);
sink(StringRefUtil::strrstr(clean, pattern)[0]);
sink(StringRefUtil::strstrCaseless(clean, pattern)[0]);
sink(StringRefUtil::strrstrCaseless(clean, pattern)[0]);
sink(StringRefUtil::strstr(clean, "missing").length());
}

void wide() {
wchar_t buffer[] = {static_cast<wchar_t>(source()), 0};
BloombergLP::bslstl::StringRefImp<wchar_t> view(buffer);
sink(view[0]); // $ ir
}