diff --git a/cpp/ql/lib/change-notes/2026-08-27-bdlbb-blob-models.md b/cpp/ql/lib/change-notes/2026-08-27-bdlbb-blob-models.md new file mode 100644 index 000000000000..f1db9d3c3116 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-08-27-bdlbb-blob-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added flow summaries for the BDE `bdlbb::Blob` segmented byte buffer (`BloombergLP::bdlbb`). Taint now flows from a blob to its bytes through the `Blob::buffer`/`BlobBuffer::data` accessor chain and through the `bdlbb::BlobUtil::copy` and `getContiguousRangeOrCopy` helpers, so a blob populated from untrusted input (for example a BlazingMQ message body read via `bmqa::Message::getData`) is tracked into the payload bytes. diff --git a/cpp/ql/lib/ext/bdlbb.model.yml b/cpp/ql/lib/ext/bdlbb.model.yml new file mode 100644 index 000000000000..135f6c631e83 --- /dev/null +++ b/cpp/ql/lib/ext/bdlbb.model.yml @@ -0,0 +1,20 @@ +# Model of the BDE bdlbb::Blob segmented byte buffer (BloombergLP::bdlbb). +# Lets taint reach a blob's payload bytes, e.g. a message body filled by bmqa::Message::getData. +extensions: + - addsTo: + pack: codeql/cpp-all + extensible: summaryModel + data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance + # Accessor chain: a tainted blob taints its buffers, and a tainted buffer taints its bytes. + - ["BloombergLP::bdlbb", "Blob", true, "buffer", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"] + - ["BloombergLP::bdlbb", "BlobBuffer", true, "data", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"] + # BlobUtil read-out: the source blob (Argument[*1]) taints the destination buffer (and the + # returned contiguous range). + - ["BloombergLP::bdlbb", "BlobUtil", true, "copy", "(char *,const Blob &,int,int)", "", "Argument[*1]", "Argument[*0]", "taint", "manual"] + - ["BloombergLP::bdlbb", "BlobUtil", true, "getContiguousRangeOrCopy", "", "", "Argument[*1]", "Argument[*0]", "taint", "manual"] + - ["BloombergLP::bdlbb", "BlobUtil", true, "getContiguousRangeOrCopy", "", "", "Argument[*1]", "ReturnValue[*]", "taint", "manual"] + # BlobUtil write-in: the source (Argument[*2]) taints the destination blob. `copy` has two + # write-in overloads, one taking a raw byte buffer and one taking another blob as the source; + # each row pins the exact signature so the int offset/length arguments are never tainted. + - ["BloombergLP::bdlbb", "BlobUtil", true, "copy", "(Blob *,int,const char *,int)", "", "Argument[*2]", "Argument[*0]", "taint", "manual"] + - ["BloombergLP::bdlbb", "BlobUtil", true, "copy", "(Blob *,int,const Blob &,int,int)", "", "Argument[*2]", "Argument[*0]", "taint", "manual"] diff --git a/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp b/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp new file mode 100644 index 000000000000..a76174c42d46 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp @@ -0,0 +1,83 @@ + +// --- stub library headers --- + +namespace bsl { + typedef unsigned long size_t; + template class allocator {}; + template struct char_traits {}; + template, class Allocator = allocator > + class basic_string { + public: + basic_string(const charT* s, const Allocator& a = Allocator()); + const charT* data() const; + size_t size() const; + }; + typedef basic_string string; +} + +namespace BloombergLP { +namespace bdlbb { + class BlobBuffer { + public: + char *data() const; + }; + + class Blob { + public: + const BlobBuffer &buffer(int index) const; + }; + + struct BlobUtil { + static void copy(char *dstBuffer, const Blob &srcBlob, int position, int length); + static void copy(Blob *dstBlob, int dstOffset, const char *srcBuffer, int length); + static void copy(Blob *dstBlob, int dstOffset, const Blob &srcBlob, int srcOffset, + int length); + static char *getContiguousRangeOrCopy(char *dstBuffer, const Blob &srcBlob, int position, + int length, int alignment); + }; +} +} + +// --- test code --- + +char *source(); +void sink(char); + +// A blob populated from a tainted buffer taints the bytes read back out of it. +void test_BlobUtil_copy() { + bsl::string s(source()); + BloombergLP::bdlbb::Blob blob; + BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size()); + char dst[16]; + BloombergLP::bdlbb::BlobUtil::copy(dst, blob, 0, 16); + sink(*dst); // $ ir +} + +void test_accessor_chain() { + bsl::string s(source()); + BloombergLP::bdlbb::Blob blob; + BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size()); + const char *p = blob.buffer(0).data(); + sink(*p); // $ ir +} + +void test_getContiguousRangeOrCopy() { + bsl::string s(source()); + BloombergLP::bdlbb::Blob blob; + BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size()); + char dst[16]; + char *r = BloombergLP::bdlbb::BlobUtil::getContiguousRangeOrCopy(dst, blob, 0, 16, 1); + sink(*r); // $ ir +} + +// A blob copied into another blob carries the taint across. +void test_BlobUtil_copy_blob_to_blob() { + bsl::string s(source()); + BloombergLP::bdlbb::Blob src; + BloombergLP::bdlbb::BlobUtil::copy(&src, 0, s.data(), s.size()); + BloombergLP::bdlbb::Blob dst; + BloombergLP::bdlbb::BlobUtil::copy(&dst, 0, src, 0, 16); + char out[16]; + BloombergLP::bdlbb::BlobUtil::copy(out, dst, 0, 16); + sink(*out); // $ ir +} diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected index b6f5f4a4452f..8989ddfa612b 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected @@ -95,7 +95,13 @@ models | 94 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | | 95 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | | 96 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | -| 97 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | +| 97 | Summary: BloombergLP::bdlbb; Blob; true; buffer; ; ; Argument[-1]; ReturnValue[*]; taint; manual | +| 98 | Summary: BloombergLP::bdlbb; BlobBuffer; true; data; ; ; Argument[-1]; ReturnValue[*]; taint; manual | +| 99 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (Blob *,int,const Blob &,int,int); ; Argument[*2]; Argument[*0]; taint; manual | +| 100 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (Blob *,int,const char *,int); ; Argument[*2]; Argument[*0]; taint; manual | +| 101 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (char *,const Blob &,int,int); ; Argument[*1]; Argument[*0]; taint; manual | +| 102 | Summary: BloombergLP::bdlbb; BlobUtil; true; getContiguousRangeOrCopy; ; ; Argument[*1]; ReturnValue[*]; taint; manual | +| 103 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | edges | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:56 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | recv_buffer | provenance | Src:MaD:56 Sink:MaD:4 | @@ -104,7 +110,7 @@ edges | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | send_buffer | provenance | Sink:MaD:4 | -| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:97 | +| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:103 | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | | @@ -144,6 +150,31 @@ edges | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | | +| bdlbb.cpp:48:16:48:23 | call to source | bdlbb.cpp:50:49:50:52 | *call to data | provenance | TaintFunction | +| bdlbb.cpp:50:37:50:41 | copy output argument | bdlbb.cpp:52:42:52:45 | *blob | provenance | | +| bdlbb.cpp:50:49:50:52 | *call to data | bdlbb.cpp:50:37:50:41 | copy output argument | provenance | MaD:100 | +| bdlbb.cpp:52:37:52:39 | copy output argument | bdlbb.cpp:53:7:53:10 | * ... | provenance | | +| bdlbb.cpp:52:42:52:45 | *blob | bdlbb.cpp:52:37:52:39 | copy output argument | provenance | MaD:101 | +| bdlbb.cpp:57:16:57:23 | call to source | bdlbb.cpp:59:49:59:52 | *call to data | provenance | TaintFunction | +| bdlbb.cpp:59:37:59:41 | copy output argument | bdlbb.cpp:60:18:60:21 | *blob | provenance | | +| bdlbb.cpp:59:49:59:52 | *call to data | bdlbb.cpp:59:37:59:41 | copy output argument | provenance | MaD:100 | +| bdlbb.cpp:60:18:60:21 | *blob | bdlbb.cpp:60:29:60:32 | *call to buffer | provenance | MaD:97 | +| bdlbb.cpp:60:18:60:38 | *call to data | bdlbb.cpp:60:18:60:38 | *call to data | provenance | | +| bdlbb.cpp:60:18:60:38 | *call to data | bdlbb.cpp:61:7:61:8 | * ... | provenance | | +| bdlbb.cpp:60:29:60:32 | *call to buffer | bdlbb.cpp:60:18:60:38 | *call to data | provenance | MaD:98 | +| bdlbb.cpp:65:16:65:23 | call to source | bdlbb.cpp:67:49:67:52 | *call to data | provenance | TaintFunction | +| bdlbb.cpp:67:37:67:41 | copy output argument | bdlbb.cpp:69:72:69:75 | *blob | provenance | | +| bdlbb.cpp:67:49:67:52 | *call to data | bdlbb.cpp:67:37:67:41 | copy output argument | provenance | MaD:100 | +| bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | provenance | | +| bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | bdlbb.cpp:70:7:70:8 | * ... | provenance | | +| bdlbb.cpp:69:72:69:75 | *blob | bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | provenance | MaD:102 | +| bdlbb.cpp:75:16:75:23 | call to source | bdlbb.cpp:77:48:77:51 | *call to data | provenance | TaintFunction | +| bdlbb.cpp:77:37:77:40 | copy output argument | bdlbb.cpp:79:46:79:48 | *src | provenance | | +| bdlbb.cpp:77:48:77:51 | *call to data | bdlbb.cpp:77:37:77:40 | copy output argument | provenance | MaD:100 | +| bdlbb.cpp:79:37:79:40 | copy output argument | bdlbb.cpp:81:42:81:44 | *dst | provenance | | +| bdlbb.cpp:79:46:79:48 | *src | bdlbb.cpp:79:37:79:40 | copy output argument | provenance | MaD:99 | +| bdlbb.cpp:81:37:81:39 | copy output argument | bdlbb.cpp:82:7:82:10 | * ... | provenance | | +| bdlbb.cpp:81:42:81:44 | *dst | bdlbb.cpp:81:37:81:39 | copy output argument | provenance | MaD:101 | | test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | | | test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | | | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:48 | @@ -532,6 +563,35 @@ nodes | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | +| bdlbb.cpp:48:16:48:23 | call to source | semmle.label | call to source | +| bdlbb.cpp:50:37:50:41 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:50:49:50:52 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:52:37:52:39 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:52:42:52:45 | *blob | semmle.label | *blob | +| bdlbb.cpp:53:7:53:10 | * ... | semmle.label | * ... | +| bdlbb.cpp:57:16:57:23 | call to source | semmle.label | call to source | +| bdlbb.cpp:59:37:59:41 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:59:49:59:52 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:60:18:60:21 | *blob | semmle.label | *blob | +| bdlbb.cpp:60:18:60:38 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:60:18:60:38 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:60:29:60:32 | *call to buffer | semmle.label | *call to buffer | +| bdlbb.cpp:61:7:61:8 | * ... | semmle.label | * ... | +| bdlbb.cpp:65:16:65:23 | call to source | semmle.label | call to source | +| bdlbb.cpp:67:37:67:41 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:67:49:67:52 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | semmle.label | *call to getContiguousRangeOrCopy | +| bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | semmle.label | *call to getContiguousRangeOrCopy | +| bdlbb.cpp:69:72:69:75 | *blob | semmle.label | *blob | +| bdlbb.cpp:70:7:70:8 | * ... | semmle.label | * ... | +| bdlbb.cpp:75:16:75:23 | call to source | semmle.label | call to source | +| bdlbb.cpp:77:37:77:40 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:77:48:77:51 | *call to data | semmle.label | *call to data | +| bdlbb.cpp:79:37:79:40 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:79:46:79:48 | *src | semmle.label | *src | +| bdlbb.cpp:81:37:81:39 | copy output argument | semmle.label | copy output argument | +| bdlbb.cpp:81:42:81:44 | *dst | semmle.label | *dst | +| bdlbb.cpp:82:7:82:10 | * ... | semmle.label | * ... | | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body | | test.cpp:7:47:7:52 | value2 | semmle.label | value2 | | test.cpp:7:64:7:69 | value2 | semmle.label | value2 | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected index 0fe13460cfbf..9a455ec8ab8c 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected @@ -4,6 +4,17 @@ | azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | | azure.cpp:287:79:287:98 | call to string | azure.cpp:287:62:287:99 | call to Url | | azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | +| bdlbb.cpp:50:49:50:52 | *call to data | bdlbb.cpp:50:37:50:41 | copy output argument | +| bdlbb.cpp:52:42:52:45 | *blob | bdlbb.cpp:52:37:52:39 | copy output argument | +| bdlbb.cpp:59:49:59:52 | *call to data | bdlbb.cpp:59:37:59:41 | copy output argument | +| bdlbb.cpp:60:18:60:21 | *blob | bdlbb.cpp:60:29:60:32 | *call to buffer | +| bdlbb.cpp:60:29:60:32 | *call to buffer | bdlbb.cpp:60:18:60:38 | *call to data | +| bdlbb.cpp:67:49:67:52 | *call to data | bdlbb.cpp:67:37:67:41 | copy output argument | +| bdlbb.cpp:69:72:69:75 | *blob | bdlbb.cpp:69:12:69:65 | *call to getContiguousRangeOrCopy | +| bdlbb.cpp:69:72:69:75 | *blob | bdlbb.cpp:69:67:69:69 | getContiguousRangeOrCopy output argument | +| bdlbb.cpp:77:48:77:51 | *call to data | bdlbb.cpp:77:37:77:40 | copy output argument | +| bdlbb.cpp:79:46:79:48 | *src | bdlbb.cpp:79:37:79:40 | copy output argument | +| bdlbb.cpp:81:42:81:44 | *dst | bdlbb.cpp:81:37:81:39 | copy output argument | | test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | | test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | | test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected b/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected index 15ae50bddc26..1fbe5da66459 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected @@ -370,6 +370,8 @@ | Dubious signature "(BN_MONT_CTX *,const BIGNUM *,int,const unsigned char *,size_t,uint32_t,uint32_t)" in summary model. | | Dubious signature "(BN_RECP_CTX *,const BIGNUM *,BN_CTX *)" in summary model. | | Dubious signature "(BUF_MEM *,size_t)" in summary model. | +| Dubious signature "(Blob *,int,const Blob &,int,int)" in summary model. | +| Dubious signature "(Blob *,int,const char *,int)" in summary model. | | Dubious signature "(BrotliBitReader *const,uint64_t,uint64_t *)" in summary model. | | Dubious signature "(BrotliDecoderState *,BrotliDecoderStateInternal *,BrotliSharedDictionaryType,size_t,const uint8_t[])" in summary model. | | Dubious signature "(BrotliDecoderState *,BrotliDecoderStateInternal *,brotli_decoder_metadata_start_func,brotli_decoder_metadata_chunk_func,void *)" in summary model. | @@ -2948,6 +2950,7 @@ | Dubious signature "(char *,char *__restrict__,int,FILE *,FILE *__restrict__)" in summary model. | | Dubious signature "(char *,char *__restrict__,size_t,const char *,const char *__restrict__,const tm *,const tm *__restrict__,locale_t)" in summary model. | | Dubious signature "(char *,char,char **)" in summary model. | +| Dubious signature "(char *,const Blob &,int,int)" in summary model. | | Dubious signature "(char *,const char *)" in summary model. | | Dubious signature "(char *,const char **,const char **,const char **,const char **,const char **)" in summary model. | | Dubious signature "(char *,const char *,char **)" in summary model. |