Skip to content

Commit c4419c3

Browse files
committed
fix: prevent buffer overread when fetching string column data
Signed-off-by: Richard Moulton <richard@rmsoftwareservices.co.uk>
1 parent 17bf83c commit c4419c3

2 files changed

Lines changed: 18 additions & 4 deletions

File tree

src/db2ia/dberror.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ static const char* getSQLType(int sqlType)
133133
return "CLOB";
134134
case SQL_DBCLOB: // SQL_DBCLOB = 15
135135
return "DBCLOB";
136-
case SQL_DATALINK: // SQL_DATALINK = 16
136+
case SQL_DATALINK: // SQL_DATALINK = 16 (pre-7.5) or -400 (7.5+)
137137
return "DATALINK";
138138
case SQL_WCHAR: // SQL_WCHAR = 17
139139
return "WCHAR";

src/db2ia/dbstmt.cc

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,9 +2347,14 @@ int DbStmt::bindColData(Napi::Env env)
23472347
sqlReturnCode = SQLBindCol(stmth, col + 1, SQL_C_CHAR, (SQLPOINTER)bindingRowInC[col], maxColLen, &dbColumn[col].rlength);
23482348
}
23492349
break;
2350-
default: // SQL_CHAR / SQL_VARCHAR
2350+
default: // SQL_CHAR / SQL_VARCHAR / SQL_BOOLEAN and other string-representable types
23512351
{
2352+
// colPrecise * 4 + 1 accounts for multi-byte character expansion + null terminator.
2353+
// Minimum of 6 ensures types like BOOLEAN (colPrecise=1, but string representation
2354+
// "FALSE" needs 6 bytes) have a large enough buffer.
23522355
maxColLen = dbColumn[col].colPrecise * 4 + 1;
2356+
if (maxColLen < 6)
2357+
maxColLen = 6;
23532358
bindingRowInC[col] = (SQLCHAR *)calloc(maxColLen, sizeof(SQLCHAR));
23542359
sqlReturnCode = SQLBindCol(stmth, col + 1, SQL_C_CHAR, (SQLPOINTER)bindingRowInC[col], maxColLen, &dbColumn[col].rlength);
23552360
}
@@ -2430,8 +2435,12 @@ int DbStmt::fetchData()
24302435
}
24312436
else
24322437
{
2438+
// rlength is the actual data length (e.g. 5 for CHAR(5) or "FALSE").
2439+
// Allocate colLen + 1 to ensure null termination, since memcpy copies
2440+
// only the data bytes without a null terminator. The extra byte is
2441+
// zero-filled by calloc.
24332442
colLen = dbColumn[col].rlength;
2434-
rowOfResultSetInC[col].data = (SQLCHAR *)calloc(colLen, sizeof(SQLCHAR));
2443+
rowOfResultSetInC[col].data = (SQLCHAR *)calloc(colLen + 1, sizeof(SQLCHAR));
24352444
memcpy(rowOfResultSetInC[col].data, bindingRowInC[col], colLen * sizeof(SQLCHAR));
24362445
rowOfResultSetInC[col].rlength = colLen;
24372446
}
@@ -2489,7 +2498,12 @@ int DbStmt::buildJsObject(Napi::Env env, Napi::Array *array)
24892498
break;
24902499
}
24912500
default:
2492-
value = Napi::String::New(env, resultSetInC[row][col].data);
2501+
// Use the known data length to bound string creation rather than relying
2502+
// on null termination, as a safeguard against buffer overreads.
2503+
if (resultSetInC[row][col].rlength == SQL_NTS)
2504+
value = Napi::String::New(env, (const char *)resultSetInC[row][col].data);
2505+
else
2506+
value = Napi::String::New(env, (const char *)resultSetInC[row][col].data, resultSetInC[row][col].rlength);
24932507
break;
24942508
}
24952509
}

0 commit comments

Comments
 (0)