Skip to content

Commit a8b95a1

Browse files
committed
Fix C++ client time column access causing UB for non-long types (#17397)
Add tsBlockColumnIndex < 0 guards to all ByTsBlockColumnIndex getter methods that were missing them. When the time pseudo-column is accessed through a typed getter other than getLong or getString, throw IoTDBException instead of causing undefined behavior via getColumn(-1). Signed-off-by: Zihan Dai <1436286758@qq.com>
1 parent 6b33dea commit a8b95a1

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

iotdb-client/client-cpp/src/main/IoTDBRpcDataSet.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ boost::optional<bool> IoTDBRpcDataSet::getBoolean(const std::string& columnName)
273273

274274
boost::optional<bool> IoTDBRpcDataSet::getBooleanByTsBlockColumnIndex(int32_t tsBlockColumnIndex) {
275275
checkRecord();
276+
if (tsBlockColumnIndex < 0) {
277+
throw IoTDBException("Cannot read boolean from time column");
278+
}
276279
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
277280
lastReadWasNull_ = false;
278281
return curTsBlock_->getColumn(tsBlockColumnIndex)->getBoolean(tsBlockIndex_);
@@ -295,6 +298,9 @@ boost::optional<double> IoTDBRpcDataSet::getDouble(const std::string& columnName
295298

296299
boost::optional<double> IoTDBRpcDataSet::getDoubleByTsBlockColumnIndex(int32_t tsBlockColumnIndex) {
297300
checkRecord();
301+
if (tsBlockColumnIndex < 0) {
302+
throw IoTDBException("Cannot read double from time column");
303+
}
298304
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
299305
lastReadWasNull_ = false;
300306
return curTsBlock_->getColumn(tsBlockColumnIndex)->getDouble(tsBlockIndex_);
@@ -317,6 +323,9 @@ boost::optional<float> IoTDBRpcDataSet::getFloat(const std::string& columnName)
317323

318324
boost::optional<float> IoTDBRpcDataSet::getFloatByTsBlockColumnIndex(int32_t tsBlockColumnIndex) {
319325
checkRecord();
326+
if (tsBlockColumnIndex < 0) {
327+
throw IoTDBException("Cannot read float from time column");
328+
}
320329
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
321330
lastReadWasNull_ = false;
322331
return curTsBlock_->getColumn(tsBlockColumnIndex)->getFloat(tsBlockIndex_);
@@ -339,6 +348,9 @@ boost::optional<int32_t> IoTDBRpcDataSet::getInt(const std::string& columnName)
339348

340349
boost::optional<int32_t> IoTDBRpcDataSet::getIntByTsBlockColumnIndex(int32_t tsBlockColumnIndex) {
341350
checkRecord();
351+
if (tsBlockColumnIndex < 0) {
352+
throw IoTDBException("Cannot read int32 from time column");
353+
}
342354
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
343355
lastReadWasNull_ = false;
344356
TSDataType::TSDataType dataType = curTsBlock_->getColumn(tsBlockColumnIndex)->getDataType();
@@ -395,6 +407,9 @@ std::shared_ptr<Binary> IoTDBRpcDataSet::getBinary(const std::string& columnName
395407

396408
std::shared_ptr<Binary> IoTDBRpcDataSet::getBinaryByTsBlockColumnIndex(int32_t tsBlockColumnIndex) {
397409
checkRecord();
410+
if (tsBlockColumnIndex < 0) {
411+
throw IoTDBException("Cannot read binary from time column");
412+
}
398413
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
399414
lastReadWasNull_ = false;
400415
return curTsBlock_->getColumn(tsBlockColumnIndex)->getBinary(tsBlockIndex_);

iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,41 @@ TEST_CASE("Test insertTablet multi datatype", "[testInsertTabletMultiDatatype]")
504504
REQUIRE(count == 100);
505505
}
506506

507+
TEST_CASE("Test boolean column via DataIterator", "[testBooleanColumnDataIterator]") {
508+
CaseReporter cr("testBooleanColumnDataIterator");
509+
string deviceId = "root.test.d1";
510+
string timeseries = "root.test.d1.s1";
511+
512+
if (session->checkTimeseriesExists(timeseries)) {
513+
session->deleteTimeseries(timeseries);
514+
}
515+
session->createTimeseries(timeseries, TSDataType::BOOLEAN, TSEncoding::PLAIN, CompressionType::SNAPPY);
516+
517+
// Insert boolean values: even timestamps get true, odd get false
518+
vector<string> measurements = {"s1"};
519+
vector<TSDataType::TSDataType> types = {TSDataType::BOOLEAN};
520+
for (int64_t time = 0; time < 10; time++) {
521+
bool val = (time % 2 == 0);
522+
vector<char*> values = {(char*)&val};
523+
session->insertRecord(deviceId, time, measurements, types, values);
524+
}
525+
526+
unique_ptr<SessionDataSet> sessionDataSet = session->executeQueryStatement("select s1 from root.test.d1");
527+
auto dataIter = sessionDataSet->getIterator();
528+
sessionDataSet->setFetchSize(1024);
529+
int count = 0;
530+
while (dataIter.next()) {
531+
bool expected = (count % 2 == 0);
532+
// Column 1 is Time, column 2 is s1
533+
REQUIRE(dataIter.getBooleanByIndex(2).value() == expected);
534+
// Accessing time column (index 1) as boolean should throw
535+
REQUIRE_THROWS_AS(dataIter.getBooleanByIndex(1), IoTDBException);
536+
count++;
537+
}
538+
REQUIRE(count == 10);
539+
session->deleteTimeseries(timeseries);
540+
}
541+
507542
TEST_CASE("Test Last query ", "[testLastQuery]") {
508543
CaseReporter cr("testLastQuery");
509544
prepareTimeseries();

0 commit comments

Comments
 (0)