diff --git a/cpp/ql/lib/change-notes/2026-09-15-bdlb-stringref.md b/cpp/ql/lib/change-notes/2026-09-15-bdlb-stringref.md new file mode 100644 index 000000000000..ceb1f2e8855e --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-09-15-bdlb-stringref.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added models for BDE character-view construction and access, and `bdlb::StringRefUtil` trimming, substring, and search operations. diff --git a/cpp/ql/lib/ext/bdlb.stringref.model.yml b/cpp/ql/lib/ext/bdlb.stringref.model.yml new file mode 100644 index 000000000000..ef8bd1d807b7 --- /dev/null +++ b/cpp/ql/lib/ext/bdlb.stringref.model.yml @@ -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"] diff --git a/cpp/ql/lib/semmle/code/cpp/models/Models.qll b/cpp/ql/lib/semmle/code/cpp/models/Models.qll index 54dc0fa0ff64..bab6c3733705 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/Models.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/Models.qll @@ -58,3 +58,5 @@ private import implementations.CAtlTemporaryFile private import implementations.CRegKey private import implementations.WinHttp private import implementations.Http + +private import implementations.BdlbStringRef diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/BdlbStringRef.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/BdlbStringRef.qll new file mode 100644 index 000000000000..dea42aef5f13 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/BdlbStringRef.qll @@ -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 = "" + } +} diff --git a/cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.expected b/cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.ql b/cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.ql new file mode 100644 index 000000000000..6bdf085e721b --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/bdlb-stringref/flow.ql @@ -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; + +import MakeTest> diff --git a/cpp/ql/test/library-tests/dataflow/bdlb-stringref/test.cpp b/cpp/ql/test/library-tests/dataflow/bdlb-stringref/test.cpp new file mode 100644 index 000000000000..c4efaf0735b2 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/bdlb-stringref/test.cpp @@ -0,0 +1,91 @@ +// Reduced declarations from bslstl_stringref.h and bdlb_stringrefutil.h. +namespace bsl { +template struct enable_if {}; +template struct enable_if { typedef T type; }; +template struct is_integral { enum { value = false }; }; +template<> struct is_integral { enum { value = true }; }; +} +namespace BloombergLP { +namespace bslmf { struct Nil {}; } +namespace bslstl { +template 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 StringRefImp(const C *, I, + typename bsl::enable_if::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 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(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(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(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(source()), 0}; + BloombergLP::bslstl::StringRefImp view(buffer); + sink(view[0]); // $ ir +}