Skip to Main Content

Programming Languages & Frameworks

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Double free (buffer overrun?) in oracle::occi::setVector

LIGER JonathanApr 8 2026

Hi,

We are running into random crashes when using oracle::occi::setVector, precisely during the destruction of the vector provided to it.

We provide a vector of transient objects (they have no connection associated) as an argument to a stored procedure.
The crash happens with the following backtrace:

#0 oracle::occi::ConnectionImpl::getOCIEnvironment() const () from ../lib64/libocci_gcc53.so.21.1

#1 oracle::occi::PObject::~PObject() () from ../lib64/libocci_gcc53.so.21.1

#2 std::destroy_at<DataRow> (__location=0x14d25e00) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/stl_construct.h:88

#3 std::_Destroy<DataRow> (__pointer=0x14d25e00) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/stl_construct.h:149

#4 std::_Destroy_aux<false>::__destroy<DataRow*> (__first=0x14d25e00, __last=0x14da4040) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/stl_construct.h:163

#5 std::_Destroy<DataRow*> (__first=<optimized out>, __last=0x14da4040) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/stl_construct.h:196

#6 std::_Destroy<DataRow*, DataRow> (__first=<optimized out>, __last=0x14da4040) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/alloc_traits.h:993

#7 std::vector<DataRow, std::allocator<DataRow> >::~vector (this=0x141a9100, __in_chrg=<optimized out>) at /opt/rh/gcc-toolset-14/root/usr/include/c++/14/bits/stl_vector.h:735

We notice that after the execution of the statement followed by the commit; at the moment when the end of the lifetime of the vector is reached, certain number of elements at the beginning of the vector were already destroyed.
In one of the latest crashes, we observed:
- For a vector with 4836 elements in it, the first 1250 elements' destructors had already run.
- As the getConnection() method doesn't return null after the destruction, we end up seeing a SEGFAULT in oracle::occi::ConnectionImpl::getOCIEnvironment(), however, as mentioned earlier, these objects are transient, so they never should have had a connection.

After performing measurements to obtain the best performance, we decided to use batches of at most 10k elements and I do not see anything against this in the documentation, but I do wonder, are we hitting a limit ?
Because if we reduce our batch size to 1k elements sent at once to the stored procedure, we do not observe crashes (or perhaps it'll take a lot longer to see them if this is a generalised bug?)

The implementation is based on the examples provided in Oracle's official documentation. Do you see anything problematic ?
Or does this simply translate to a buffer overrun erro in OCCI or OCI and needs a fix in the instantclient library ?

Any help is much appreciated.
Thanks in advance.

Please find code for reproduction along complementary information below:
The db instance is Oracle 19c. version of occi is 21.21. We're running on RHEL 8.

-- DB types
create or replace TYPE DATA_ROW AS OBJECT
(
UNIQUE_ID VARCHAR2(48),
KEY_PART VARCHAR2(4000),
VALUE_PART VARCHAR2(4000)
);

create or replace TYPE DATA_ROW_ARRAY AS TABLE OF DATA_ROW;

PROCEDURE save_data_rows(
IN_DATA_ROWS IN DATA_ROW_ARRAY
) AS
BEGIN
-- PERFORMS DML OPERATIONS
END;

The application is compiled with the following flags: g++ -g -O3 -I./oci-21.21/include/ -L./oci-21.21/lib/ main.cpp DataRow.cpp -ltcmalloc_and_profiler -lk5crypto -llber -lldap -lclntsh -lclntshcore -lnnz21 -locci_gcc53 -lociei -lpthread -latomic -std=gnu++20 -D_GNU_SOURCE -DLINUX -D_REENTRANT -o occi_test

-- main.cpp
#ifndef OCCI_ORACLE
# include <occi.h>
#endif

#include "DataRow.h"

int main(void)
{
const auto envMode = static_cast<oracle::occi::Environment::Mode>(oracle::occi::Environment::Mode::OBJECT | oracle::occi::Environment::Mode::THREADED_MUTEXED);
oracle::occi::Environment* env = oracle::occi::Environment::createEnvironment(envMode);

           `oracle::occi::Map* mapOCCI = env->getMap();`  
           `mapOCCI->put(login, "DATA_ROW", &CDataRow::readSQL, &CDataRow::writeSQL);`

           `oracle::occi::StatelessConnectionPool* scnp = env->createStatelessConnectionPool(login,`  
                                                                                                                                                                               `password,`  
                                                                                                                                                                               `connectString,`  
                                                                                                                                                                               `10,`  
                                                                                                                                                                               `1,`  
                                                                                                                                                                               `1u,`  
                                                                                                                                                                               `oracle::occi::StatelessConnectionPool::HOMOGENEOUS);`

           `scnp->setTimeOut(30);`  
           `scnp->setStmtCacheSize(10);`


           `// In production, the lines below run in a separate thread and at the end of the function that makes the calls below, when the vector dataRows is being destroyed, we sometimes run into the aforementioned double free`  
           `{`  
                           `const static std::string sql = "BEGIN PARAMETERS.save_data_rows(:dataRows); END;";`  
                           `const static std::string stmtTag = "saveDataRowsStmt";`

                           `oracle::occi::Connection* cn = scnp->getConnection();`  
                           `oracle::occi::Statement* stmt = cn->createStatement(sql, stmtTag);`

                           `constexpr std::size_t totalRows = 10000;`  
                           `std::vector<DataRow> dataRows {};`  
                           `dataRows.reserve(totalLines);`

                           `for (auto i = 1; i <= totalRows; ++i)`  
                           `{`  
                                           `std::string unique_id = "TEST_ID_" + std::to_string(i);`  
                                           `std::string key = "TEST_KEY_" + std::to_string(i);`  
                                           `std::string value = "TEST_VALUE_" + std::to_string(i);`

                                           `dataRows.emplace_back(std::move(id), std::move(key), std::move(value));`  
                           `}`

                           `std::vector<oracle::occi::PObject*> dataRowsParam(dataRows.size());`  
                           `std::transform(dataRows.begin(), dataRows.end(), dataRowsParam.begin(), [](DataRow& p) -> DataRow* { return &p; });`  
                           `oracle::occi::setVector(stmt, 1, dataRowsParam, login, "DATA_ROW_ARRAY");`

                           `stmt->execute();`  
                           `cn->commit();`

                           `cn->terminateStatement(stmt, stmtTag);`  
                           `scnp->releaseConnection(cn);`  
           `}`  

}

-- DataRow.h
#pragma once

#ifndef OCCI_ORACLE
# include <occi.h>
#endif

class DataRow;

class CDataRow : public oracle::occi::PObject {

private:
OCCI_STD_NAMESPACE::string UNIQUE_ID;
OCCI_STD_NAMESPACE::string KEY_PART;
OCCI_STD_NAMESPACE::string VALUE_PART;

public:
OCCI_STD_NAMESPACE::string getUniqueId() const;
void setUniqueId(const OCCI_STD_NAMESPACE::string &value);

OCCI_STD_NAMESPACE::string getKeyPart() const;
void setKeyPart(const OCCI_STD_NAMESPACE::string &value);

OCCI_STD_NAMESPACE::string getValuePart() const;
void setValuePart(const OCCI_STD_NAMESPACE::string &value);

void *operator new(size_t size);
void *operator new(size_t size, const oracle::occi::Connection * sess,
const OCCI_STD_NAMESPACE::string& table);
void *operator new(size_t, void *ctxOCCI_);
void *operator new(size_t size, const oracle::occi::Connection *sess,
const OCCI_STD_NAMESPACE::string &tableName,
const OCCI_STD_NAMESPACE::string &typeName,
const OCCI_STD_NAMESPACE::string &tableSchema,
const OCCI_STD_NAMESPACE::string &typeSchema);

OCCI_STD_NAMESPACE::string getSQLTypeName() const;

void getSQLTypeName(oracle::occi::Environment *env, void **schemaName,
unsigned int &schemaNameLen, void **typeName,
unsigned int &typeNameLen) const;

CDataRow();
CDataRow(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { };
static void *readSQL(void *ctxOCCI_);
virtual void readSQL(oracle::occi::AnyData& streamOCCI_);
static void writeSQL(void *objOCCI_, void *ctxOCCI_);
virtual void writeSQL(oracle::occi::AnyData& streamOCCI_);
~CDataRow();
};

class DataRow : public CDataRow
{ public:
DataRow(const std::string& uniqueId, const std::string& keyPart, const std::string& valuePart);
void displayInfo();
DataRow(void *ctxOCCI_);
};

-- DataRow.cpp
#include "DataRow.h"

#include <iostream>

OCCI_STD_NAMESPACE::string CDataRow::getUniqueId() const
{
return UNIQUE_ID;
}

void CDataRow::setUniqueId(const OCCI_STD_NAMESPACE::string &value)
{
UNIQUE_ID = value;
}

OCCI_STD_NAMESPACE::string CDataRow::getKeyPart() const
{
return KEY_PART;
}

void CDataRow::setKeyPart(const OCCI_STD_NAMESPACE::string &value)
{
KEY_PART = value;
}

OCCI_STD_NAMESPACE::string CDataRow::getValuePart() const
{
return VALUE_PART;
}

void CDataRow::setValuePart(const OCCI_STD_NAMESPACE::string &value)
{
VALUE_PART = value;
}

void *CDataRow::operator new(std::size_t size)
{
return oracle::occi::PObject::operator new(size);
}

void *CDataRow::operator new(std::size_t size, const oracle::occi::Connection *
sess, const OCCI_STD_NAMESPACE::string& table)
{
return oracle::occi::PObject::operator new(size, sess, table,
(char *) "DATA_ROW");
}

void *CDataRow::operator new(std::size_t size, void *ctxOCCI_)
{
return oracle::occi::PObject::operator new(size, ctxOCCI_);
}

void *CDataRow::operator new(std::size_t size,
const oracle::occi::Connection *sess,
const OCCI_STD_NAMESPACE::string &tableName,
const OCCI_STD_NAMESPACE::string &typeName,
const OCCI_STD_NAMESPACE::string &tableSchema,
const OCCI_STD_NAMESPACE::string &typeSchema)
{
return oracle::occi::PObject::operator new(size, sess, tableName,
typeName, tableSchema, typeSchema);
}

OCCI_STD_NAMESPACE::string CDataRow::getSQLTypeName() const
{
return OCCI_STD_NAMESPACE::string("DATA_ROW");
}

void CDataRow::getSQLTypeName(oracle::occi::Environment *env,
void **schemaName, unsigned int &schemaNameLen, void **typeName,
unsigned int &typeNameLen) const
{
PObject::getSQLTypeName(env, &CDataRow::readSQL, schemaName,
schemaNameLen, typeName, typeNameLen);
}

CDataRow::CDataRow()
{
}

void *CDataRow::readSQL(void *ctxOCCI_)
{
DataRow *objOCCI_ = new(ctxOCCI_) DataRow(ctxOCCI_);
oracle::occi::AnyData streamOCCI_(ctxOCCI_);

try
{
if (streamOCCI_.isNull())
objOCCI_->setNull();
else
objOCCI_->readSQL(streamOCCI_);
}
catch (oracle::occi::SQLException& excep)
{
delete objOCCI_;
excep.setErrorCtx(ctxOCCI_);
return (void *)NULL;
}
return (void *)objOCCI_;
}

void CDataRow::readSQL(oracle::occi::AnyData& streamOCCI_)
{
UNIQUE_ID = streamOCCI_.getString();
KEY_PART = streamOCCI_.getString();
VALUE_PART = streamOCCI_.getString();
}

void CDataRow::writeSQL(void *objectOCCI_, void *ctxOCCI_){
CDataRow *objOCCI_ = (CDataRow *) objectOCCI_;
oracle::occi::AnyData streamOCCI_(ctxOCCI_);

try
{
if (objOCCI_->isNull())
streamOCCI_.setNull();
else
objOCCI_->writeSQL(streamOCCI_);
}
catch (oracle::occi::SQLException& excep)
{
excep.setErrorCtx(ctxOCCI_);
}
return;
}

void CDataRow::writeSQL(oracle::occi::AnyData& streamOCCI_)
{
streamOCCI_.setString(UNIQUE_ID);
streamOCCI_.setString(KEY_PART);
streamOCCI_.setString(VALUE_PART);
}

CDataRow::~CDataRow()
{
int i;
}

DataRow::DataRow(const std::string& uniqueId, const std::string& keyPart, const std::string& valuePart)
{
setUniqueId(uniqueId);
setKeyPart(keyPart);
setValuePart(valuePart);
}

/* display all the information in DataRow */
void DataRow::displayInfo()
{
std::cout << "UNIQUE_ID is" << getUniqueId() << std::endl;
std::cout << "KEY_PART is" << getKeyPart() << std::endl;
std::cout << "VALUE_PART is" << getValuePart() << std::endl;
}

DataRow::DataRow(void *ctxOCCI_):CDataRow(ctxOCCI_)
{
}

Comments
Post Details
Added on Apr 8 2026
0 comments
54 views