This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new 8d1ad63e955 [To dev/1.3] enhance cppclient tsblock deserialize
validation (#17464) (#17518)
8d1ad63e955 is described below
commit 8d1ad63e955a324be605baf6411544e563da1b6d
Author: Hongzhi Gao <[email protected]>
AuthorDate: Tue Apr 21 11:38:04 2026 +0800
[To dev/1.3] enhance cppclient tsblock deserialize validation (#17464)
(#17518)
* fix tsblock deserialize
* fix ut error on win
* Revert "fix ut error on win"
This reverts commit 34b8de482c864abd64d721bdd51fe08e18b742b8.
---
iotdb-client/client-cpp/src/main/ColumnDecoder.cpp | 3 +
iotdb-client/client-cpp/src/main/Common.cpp | 46 ++++++++++++++-
iotdb-client/client-cpp/src/main/Common.h | 8 +++
iotdb-client/client-cpp/src/main/TsBlock.cpp | 12 ++++
iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp | 67 ++++++++++++++++++++++
5 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/iotdb-client/client-cpp/src/main/ColumnDecoder.cpp
b/iotdb-client/client-cpp/src/main/ColumnDecoder.cpp
index e45cb49409a..32f29d876f3 100644
--- a/iotdb-client/client-cpp/src/main/ColumnDecoder.cpp
+++ b/iotdb-client/client-cpp/src/main/ColumnDecoder.cpp
@@ -151,6 +151,9 @@ std::unique_ptr<Column>
BinaryArrayColumnDecoder::readColumn(
if (!nullIndicators.empty() && nullIndicators[i]) continue;
int32_t length = buffer.getInt();
+ if (length < 0) {
+ throw IoTDBException("BinaryArrayColumnDecoder: negative TEXT
length");
+ }
std::vector<uint8_t> value(length);
for (int32_t j = 0; j < length; j++) {
diff --git a/iotdb-client/client-cpp/src/main/Common.cpp
b/iotdb-client/client-cpp/src/main/Common.cpp
index 38e8a31d2ff..f58b6cc21dd 100644
--- a/iotdb-client/client-cpp/src/main/Common.cpp
+++ b/iotdb-client/client-cpp/src/main/Common.cpp
@@ -19,6 +19,7 @@
#include "Common.h"
#include <boost/date_time/gregorian/gregorian.hpp>
+#include <stdexcept>
int32_t parseDateExpressionToInt(const boost::gregorian::date& date) {
if (date.is_not_a_date()) {
@@ -292,6 +293,10 @@ double MyStringBuffer::getDouble() {
}
char MyStringBuffer::getChar() {
+ if (pos >= str.size()) {
+ throw IoTDBException("MyStringBuffer::getChar: read past end (pos=" +
std::to_string(pos) +
+ ", size=" + std::to_string(str.size()) + ")");
+ }
return str[pos++];
}
@@ -300,8 +305,16 @@ bool MyStringBuffer::getBool() {
}
std::string MyStringBuffer::getString() {
- size_t len = getInt();
- size_t tmpPos = pos;
+ const int lenInt = getInt();
+ if (lenInt < 0) {
+ throw IoTDBException("MyStringBuffer::getString: negative length");
+ }
+ const size_t len = static_cast<size_t>(lenInt);
+ if (pos > str.size() || len > str.size() - pos) {
+ throw IoTDBException("MyStringBuffer::getString: length exceeds buffer
(pos=" + std::to_string(pos) +
+ ", len=" + std::to_string(len) + ", size=" +
std::to_string(str.size()) + ")");
+ }
+ const size_t tmpPos = pos;
pos += len;
return str.substr(tmpPos, len);
}
@@ -350,6 +363,10 @@ void MyStringBuffer::checkBigEndian() {
}
const char* MyStringBuffer::getOrderedByte(size_t len) {
+ if (pos > str.size() || len > str.size() - pos) {
+ throw IoTDBException("MyStringBuffer::getOrderedByte: read past end
(pos=" + std::to_string(pos) +
+ ", len=" + std::to_string(len) + ", size=" +
std::to_string(str.size()) + ")");
+ }
const char* p = nullptr;
if (isBigEndian) {
p = str.c_str() + pos;
@@ -454,3 +471,28 @@ const std::vector<char>& BitMap::getByteArray() const {
size_t BitMap::getSize() const {
return this->size;
}
+
+TEndPoint UrlUtils::parseTEndPointIpv4AndIpv6Url(const std::string&
endPointUrl) {
+ TEndPoint endPoint;
+ const size_t colonPos = endPointUrl.find_last_of(':');
+ if (colonPos == std::string::npos) {
+ endPoint.__set_ip(endPointUrl);
+ endPoint.__set_port(0);
+ return endPoint;
+ }
+ std::string ip = endPointUrl.substr(0, colonPos);
+ const std::string portStr = endPointUrl.substr(colonPos + 1);
+ try {
+ const int port = std::stoi(portStr);
+ endPoint.__set_port(port);
+ } catch (const std::logic_error&) {
+ endPoint.__set_ip(endPointUrl);
+ endPoint.__set_port(0);
+ return endPoint;
+ }
+ if (ip.size() >= 2 && ip.front() == '[' && ip.back() == ']') {
+ ip = ip.substr(1, ip.size() - 2);
+ }
+ endPoint.__set_ip(ip);
+ return endPoint;
+}
diff --git a/iotdb-client/client-cpp/src/main/Common.h
b/iotdb-client/client-cpp/src/main/Common.h
index a9f4552ecc5..af9cf46e62e 100644
--- a/iotdb-client/client-cpp/src/main/Common.h
+++ b/iotdb-client/client-cpp/src/main/Common.h
@@ -480,5 +480,13 @@ public:
static std::shared_ptr<TSFetchResultsResp> getTSFetchResultsResp(const
TSStatus& status);
};
+class UrlUtils {
+public:
+ UrlUtils() = delete;
+
+ /** Parse host:port; aligns with Java
UrlUtils.parseTEndPointIpv4AndIpv6Url plus test edge cases. */
+ static TEndPoint parseTEndPointIpv4AndIpv6Url(const std::string&
endPointUrl);
+};
+
#endif
diff --git a/iotdb-client/client-cpp/src/main/TsBlock.cpp
b/iotdb-client/client-cpp/src/main/TsBlock.cpp
index 7c2bac272a6..92afbef3f27 100644
--- a/iotdb-client/client-cpp/src/main/TsBlock.cpp
+++ b/iotdb-client/client-cpp/src/main/TsBlock.cpp
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+#include <cstdint>
#include <stdexcept>
#include <algorithm>
#include "TsBlock.h"
@@ -34,6 +35,14 @@ std::shared_ptr<TsBlock> TsBlock::deserialize(const
std::string& data) {
// Read value column count
int32_t valueColumnCount = buffer.getInt();
+ if (valueColumnCount < 0) {
+ throw IoTDBException("TsBlock::deserialize: negative
valueColumnCount");
+ }
+ const int64_t minHeaderBytes =
+ 9LL + 2LL * static_cast<int64_t>(valueColumnCount);
+ if (minHeaderBytes > static_cast<int64_t>(data.size())) {
+ throw IoTDBException("TsBlock::deserialize: truncated header");
+ }
// Read value column data types
std::vector<TSDataType::TSDataType> valueColumnDataTypes(valueColumnCount);
@@ -43,6 +52,9 @@ std::shared_ptr<TsBlock> TsBlock::deserialize(const
std::string& data) {
// Read position count
int32_t positionCount = buffer.getInt();
+ if (positionCount < 0) {
+ throw IoTDBException("TsBlock::deserialize: negative positionCount");
+ }
// Read column encodings
std::vector<ColumnEncoding> columnEncodings(valueColumnCount + 1);
diff --git a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
index 09154b868e4..b2db95cc4ec 100644
--- a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
+++ b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
@@ -19,6 +19,8 @@
#include "catch.hpp"
#include "Session.h"
+#include "TsBlock.h"
+#include <sstream>
using namespace std;
@@ -728,3 +730,68 @@ TEST_CASE("Test executeLastDataQuery ",
"[testExecuteLastDataQuery]") {
sessionDataSet->setFetchSize(1024);
REQUIRE(sessionDataSet->hasNext() == false);
}
+
+// Helper function for comparing TEndPoint with detailed error message
+void assertTEndPointEqual(const TEndPoint& actual,
+ const std::string& expectedIp,
+ int expectedPort,
+ const char* file,
+ int line) {
+ if (actual.ip != expectedIp || actual.port != expectedPort) {
+ std::stringstream ss;
+ ss << "\nTEndPoint mismatch:\nExpected: " << expectedIp << ":" <<
expectedPort
+ << "\nActual: " << actual.ip << ":" << actual.port;
+ Catch::SourceLineInfo location(file, line);
+ Catch::AssertionHandler handler("TEndPoint comparison", location,
ss.str(), Catch::ResultDisposition::Normal);
+ handler.handleMessage(Catch::ResultWas::ExplicitFailure, ss.str());
+ handler.complete();
+ }
+}
+
+// Macro to simplify test assertions
+#define REQUIRE_TENDPOINT(actual, expectedIp, expectedPort) \
+ assertTEndPointEqual(actual, expectedIp, expectedPort, __FILE__, __LINE__)
+
+TEST_CASE("UrlUtils - parseTEndPointIpv4AndIpv6Url", "[UrlUtils]") {
+ // Test valid IPv4 addresses
+ SECTION("Valid IPv4") {
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("192.168.1.1:8080"),
"192.168.1.1", 8080);
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("10.0.0.1:80"),
"10.0.0.1", 80);
+ }
+
+ // Test valid IPv6 addresses
+ SECTION("Valid IPv6") {
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("[2001:db8::1]:8080"),
"2001:db8::1", 8080);
+ REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("[::1]:80"),
"::1", 80);
+ }
+
+ // Test hostnames
+ SECTION("Hostnames") {
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("localhost:8080"),
"localhost", 8080);
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("example.com:443"),
"example.com", 443);
+ }
+
+ // Test edge cases
+ SECTION("Edge cases") {
+ REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url(""), "", 0);
+ REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("127.0.0.1"),
"127.0.0.1", 0);
+ }
+
+ // Test invalid inputs
+ SECTION("Invalid inputs") {
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("192.168.1.1:abc"),
"192.168.1.1:abc", 0);
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("]invalid[:80"),
"]invalid[", 80);
+ }
+
+ // Test port ranges
+ SECTION("Port ranges") {
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("localhost:0"),
"localhost", 0);
+
REQUIRE_TENDPOINT(UrlUtils::parseTEndPointIpv4AndIpv6Url("127.0.0.1:65535"),
"127.0.0.1", 65535);
+ }
+}
+
+TEST_CASE("TsBlock deserialize rejects truncated malicious payload",
"[TsBlockDeserialize]") {
+ std::string data(18, '\0');
+ data[3] = '\x10';
+ REQUIRE_THROWS_AS(TsBlock::deserialize(data), IoTDBException);
+}