@@ -236,9 +236,7 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
236
236
BugType BT_ResourceLeak{this , " Resource leak" , " Stream handling error" ,
237
237
/* SuppressOnSink =*/ true };
238
238
BugType BT_SizeNull{this , " NULL size pointer" , " Stream handling error" };
239
- BugType BT_SizeGreaterThanBufferSize{
240
- this , " Size greater than the allocated buffer size" ,
241
- categories::MemoryError};
239
+ BugType BT_IllegalSize{this , " Invalid buffer size" , categories::MemoryError};
242
240
243
241
public:
244
242
void checkPreCall (const CallEvent &Call, CheckerContext &C) const ;
@@ -1221,28 +1219,50 @@ StreamChecker::ensurePtrNotNull(SVal PtrVal, const Expr *PtrExpr,
1221
1219
ProgramStateRef StreamChecker::ensureGetdelimBufferAndSizeCorrect (
1222
1220
SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
1223
1221
const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
1224
- // If the argument `*n` is non-zero, `*lineptr` must point to an object of
1225
- // size at least `*n` bytes, or a `NULL` pointer.
1226
1222
static constexpr char SizeGreaterThanBufferSize[] =
1227
1223
" The buffer from the first argument is smaller than the size "
1228
1224
" specified by the second parameter" ;
1225
+ static constexpr char SizeUndef[] =
1226
+ " The buffer from the first argument is not NULL, but the size specified "
1227
+ " by the second parameter is undefined." ;
1228
+
1229
+ auto EmitBugReport = [this , &C, SizePtrExpr,
1230
+ LinePtrPtrExpr](const ProgramStateRef &BugState,
1231
+ const char *ErrMsg) {
1232
+ if (ExplodedNode *N = C.generateErrorNode (BugState)) {
1233
+ auto R =
1234
+ std::make_unique<PathSensitiveBugReport>(BT_IllegalSize, ErrMsg, N);
1235
+ bugreporter::trackExpressionValue (N, SizePtrExpr, *R);
1236
+ bugreporter::trackExpressionValue (N, LinePtrPtrExpr, *R);
1237
+ C.emitReport (std::move (R));
1238
+ }
1239
+ };
1229
1240
1230
1241
// We have a pointer to a pointer to the buffer, and a pointer to the size.
1231
1242
// We want what they point at.
1232
- auto LinePtrSVal = getPointeeDefVal (LinePtrPtrSVal, State);
1233
- auto NSVal = getPointeeDefVal (SizePtrSVal, State);
1234
- if (!LinePtrSVal || !NSVal)
1243
+ auto LinePtrSVal = getPointeeVal (LinePtrPtrSVal, State)-> getAs <DefinedSVal>( );
1244
+ auto NSVal = getPointeeVal (SizePtrSVal, State);
1245
+ if (!LinePtrSVal || !NSVal || NSVal-> isUnknown () )
1235
1246
return nullptr ;
1236
1247
1237
1248
assert (LinePtrPtrExpr && SizePtrExpr);
1238
1249
1239
1250
const auto [LinePtrNotNull, LinePtrNull] = State->assume (*LinePtrSVal);
1240
1251
if (LinePtrNotNull && !LinePtrNull) {
1252
+ // If `*lineptr` is not null, but `*n` is undefined, there is UB.
1253
+ if (NSVal->isUndef ()) {
1254
+ EmitBugReport (LinePtrNotNull, SizeUndef);
1255
+ return nullptr ;
1256
+ }
1257
+
1258
+ // If it is defined, and known, its size must be less than or equal to
1259
+ // the buffer size.
1260
+ auto NDefSVal = NSVal->getAs <DefinedSVal>();
1241
1261
auto &SVB = C.getSValBuilder ();
1242
1262
auto LineBufSize =
1243
1263
getDynamicExtent (LinePtrNotNull, LinePtrSVal->getAsRegion (), SVB);
1244
1264
auto LineBufSizeGtN = SVB.evalBinOp (LinePtrNotNull, BO_GE, LineBufSize,
1245
- *NSVal , SVB.getConditionType ())
1265
+ *NDefSVal , SVB.getConditionType ())
1246
1266
.getAs <DefinedOrUnknownSVal>();
1247
1267
if (!LineBufSizeGtN) {
1248
1268
return LinePtrNotNull;
@@ -1251,13 +1271,7 @@ ProgramStateRef StreamChecker::ensureGetdelimBufferAndSizeCorrect(
1251
1271
return LineBufSizeOk;
1252
1272
}
1253
1273
1254
- if (ExplodedNode *N = C.generateErrorNode (LinePtrNotNull)) {
1255
- auto R = std::make_unique<PathSensitiveBugReport>(
1256
- BT_SizeGreaterThanBufferSize, SizeGreaterThanBufferSize, N);
1257
- bugreporter::trackExpressionValue (N, SizePtrExpr, *R);
1258
- bugreporter::trackExpressionValue (N, LinePtrPtrExpr, *R);
1259
- C.emitReport (std::move (R));
1260
- }
1274
+ EmitBugReport (LinePtrNotNull, SizeGreaterThanBufferSize);
1261
1275
return nullptr ;
1262
1276
}
1263
1277
return State;
@@ -1337,7 +1351,7 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,
1337
1351
// The buffer size `*n` must be enough to hold the whole line, and
1338
1352
// greater than the return value, since it has to account for '\0'.
1339
1353
auto SizePtrSval = Call.getArgSVal (1 );
1340
- auto NVal = getPointeeDefVal (SizePtrSval, State);
1354
+ auto NVal = getPointeeVal (SizePtrSval, State);
1341
1355
if (NVal) {
1342
1356
StateNotFailed = StateNotFailed->assume (
1343
1357
E.SVB
@@ -1362,7 +1376,7 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,
1362
1376
StateFailed = E.setStreamState (
1363
1377
StateFailed, StreamState::getOpened (Desc, NewES, !NewES.isFEof ()));
1364
1378
// On failure, the content of the buffer is undefined.
1365
- if (auto NewLinePtr = getPointeeDefVal (Call.getArgSVal (0 ), State)) {
1379
+ if (auto NewLinePtr = getPointeeVal (Call.getArgSVal (0 ), State)) {
1366
1380
StateFailed = StateFailed->bindLoc (*NewLinePtr, UndefinedVal (),
1367
1381
C.getLocationContext ());
1368
1382
}
0 commit comments