clang: lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

24#include "llvm/ADT/STLExtras.h"

25#include "llvm/ADT/SmallString.h"

26#include "llvm/ADT/StringExtras.h"

27#include "llvm/Support/raw_ostream.h"

28#include

29

30using namespace clang;

31using namespace ento;

32

34

35

37

38

39

41};

42

43namespace {

44

45class UnixAPIMisuseChecker

46 : public Checker<check::PreCall, check::ASTDecl> {

48 const BugType BT_getline{this, "Improper use of getdelim",

50 const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",

53 mutable std::optional<uint64_t> Val_O_CREAT;

54

58 std::optional<std::reference_wrapper> BT =

59 std::nullopt) const;

60

62 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,

64

65public:

68

70

75

78

81};

82

83class UnixAPIPortabilityChecker : public Checker< check::PreStmt > {

84public:

86

87private:

88 const BugType BT_mallocZero{

89 this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",

91

99

102 const Expr *arg,

103 const char *fn_name) const;

106 const unsigned numArgs,

107 const unsigned sizeArg,

108 const char *fn) const;

109};

110

111}

112

115 const StringRef PtrDescr,

116 std::optional<std::reference_wrapper> BT) const {

118 if (!Ptr)

119 return State;

120

121 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);

122 if (!PtrNotNull && PtrNull) {

123 if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {

124 auto R = std::make_unique(

125 BT.value_or(std::cref(BT_ArgumentNull)),

126 (PtrDescr + " pointer might be NULL.").str(), N);

127 if (PtrExpr)

129 C.emitReport(std::move(R));

130 }

131 return nullptr;

132 }

133

134 return PtrNotNull;

135}

136

140

141

143

144 if (!Val_O_CREAT) {

146 llvm::Triple::Apple)

147 Val_O_CREAT = 0x0200;

148 }

149}

150

151

152

153

154

155void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,

157 const FunctionDecl *FD = dyn_cast_if_present(Call.getDecl());

158 if (!FD || FD->getKind() != Decl::Function)

159 return;

160

161

162

164 if (isa_and_nonnull(NamespaceCtx))

165 return;

166

167 StringRef FName = C.getCalleeName(FD);

168 if (FName.empty())

169 return;

170

171 if (FName == "open")

172 CheckOpen(C, Call);

173

174 else if (FName == "openat")

175 CheckOpenAt(C, Call);

176

177 else if (FName == "pthread_once")

178 CheckPthreadOnce(C, Call);

179

180 else if (is_contained({"getdelim", "getline"}, FName))

181 CheckGetDelim(C, Call);

182}

183void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,

185 const char *Msg,

188 if (!N)

189 return;

190

191 auto Report = std::make_unique(BT_open, Msg, N);

192 Report->addRange(SR);

193 C.emitReport(std::move(Report));

194}

195

199}

200

204}

205

206void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,

209

210

211 unsigned int FlagsArgIndex;

212 const char *VariantName;

213 switch (Variant) {

215 FlagsArgIndex = 1;

216 VariantName = "open";

217 break;

219 FlagsArgIndex = 2;

220 VariantName = "openat";

221 break;

222 };

223

224

225 unsigned int MinArgCount = FlagsArgIndex + 1;

226

227

228

229 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;

230

231

232 unsigned int MaxArgCount = CreateModeArgIndex + 1;

233

235

236 if (Call.getNumArgs() < MinArgCount) {

237

238 return;

239 } else if (Call.getNumArgs() == MaxArgCount) {

240 const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);

244 llvm::raw_svector_ostream OS(SBuf);

245 OS << "The " << CreateModeArgIndex + 1

246 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)

247 << " argument to '" << VariantName << "' is not an integer";

248

249 ReportOpenBug(C, state,

250 SBuf.c_str(),

252 return;

253 }

254 } else if (Call.getNumArgs() > MaxArgCount) {

256 llvm::raw_svector_ostream OS(SBuf);

257 OS << "Call to '" << VariantName << "' with more than " << MaxArgCount

258 << " arguments";

259

260 ReportOpenBug(C, state, SBuf.c_str(),

261 Call.getArgExpr(MaxArgCount)->getSourceRange());

262 return;

263 }

264

265 if (!Val_O_CREAT) {

266 return;

267 }

268

269

270 const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);

271 const SVal V = Call.getArgSVal(FlagsArgIndex);

272 if (!isa(V)) {

273

274

275 return;

276 }

278 NonLoc ocreateFlag = C.getSValBuilder()

279 .makeIntVal(*Val_O_CREAT, oflagsEx->getType())

280 .castAs();

281 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,

282 oflags, ocreateFlag,

285 return;

287

288

290 std::tie(trueState, falseState) = state->assume(maskedFlags);

291

292

293

294 if (!(trueState && !falseState))

295 return;

296

297 if (Call.getNumArgs() < MaxArgCount) {

299 llvm::raw_svector_ostream OS(SBuf);

300 OS << "Call to '" << VariantName << "' requires a "

301 << CreateModeArgIndex + 1

302 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)

303 << " argument when the 'O_CREAT' flag is set";

304 ReportOpenBug(C, trueState,

305 SBuf.c_str(),

307 }

308}

309

310

311

312

313

314ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(

315 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,

317 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =

318 "The buffer from the first argument is smaller than the size "

319 "specified by the second parameter";

320 static constexpr llvm::StringLiteral SizeUndef =

321 "The buffer from the first argument is not NULL, but the size specified "

322 "by the second parameter is undefined.";

323

324 auto EmitBugReport = [this, &C, SizePtrExpr, LinePtrPtrExpr](

326 if (ExplodedNode *N = C.generateErrorNode(BugState)) {

327 auto R = std::make_unique(BT_getline, ErrMsg, N);

330 C.emitReport(std::move(R));

331 }

332 };

333

334

335

338 if (!LinePtrSVal || !NSVal || NSVal->isUnknown())

339 return nullptr;

340

341 assert(LinePtrPtrExpr && SizePtrExpr);

342

343 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);

344 if (LinePtrNotNull && !LinePtrNull) {

345

347 EmitBugReport(LinePtrNotNull, SizeUndef);

348 return nullptr;

349 }

350

351

352

354 auto &SVB = C.getSValBuilder();

355 auto LineBufSize =

356 getDynamicExtent(LinePtrNotNull, LinePtrSVal->getAsRegion(), SVB);

357 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,

358 *NDefSVal, SVB.getConditionType())

360 if (!LineBufSizeGtN)

361 return LinePtrNotNull;

362 if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))

363 return LineBufSizeOk;

364

365 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);

366 return nullptr;

367 }

368 return State;

369}

370

371void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,

374

375

376 SVal SizePtrSval = Call.getArgSVal(1);

377 State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");

378 if (!State)

379 return;

380

381

382 SVal LinePtrPtrSVal = Call.getArgSVal(0);

383 State =

384 EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");

385 if (!State)

386 return;

387

388 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,

389 Call.getArgExpr(0),

390 Call.getArgExpr(1), C, State);

391 if (!State)

392 return;

393

394 C.addTransition(State);

395}

396

397

398

399

400

401void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,

403

404

405

406

407 if (Call.getNumArgs() < 1)

408 return;

409

410

411

413 const MemRegion *R = Call.getArgSVal(0).getAsRegion();

414 if (!R || !isa(R->getMemorySpace()))

415 return;

416

418 if (!N)

419 return;

420

422 llvm::raw_svector_ostream os(S);

423 os << "Call to 'pthread_once' uses";

424 if (const VarRegion *VR = dyn_cast(R))

425 os << " the local variable '" << VR->getDecl()->getName() << '\'';

426 else

427 os << " stack allocated memory";

428 os << " for the \"control\" value. Using such transient memory for "

429 "the control value is potentially dangerous.";

430 if (isa(R) && isa(R->getMemorySpace()))

431 os << " Perhaps you intended to declare the variable as 'static'?";

432

433 auto report =

434 std::make_unique(BT_pthreadOnce, os.str(), N);

435 report->addRange(Call.getArgExpr(0)->getSourceRange());

436 C.emitReport(std::move(report));

437}

438

439

440

441

442

443

444

445

446

447

448

450 const SVal argVal,

453 std::tie(*trueState, *falseState) =

455

456 return (*falseState && !*trueState);

457}

458

459

460

461

462bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(

465 const Expr *arg,

466 const char *fn_name) const {

467 ExplodedNode *N = C.generateErrorNode(falseState);

468 if (!N)

469 return false;

470

472 llvm::raw_svector_ostream os(S);

473 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";

474 auto report =

475 std::make_unique(BT_mallocZero, os.str(), N);

476

477 report->addRange(arg->getSourceRange());

479 C.emitReport(std::move(report));

480

481 return true;

482}

483

484

485

486void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,

488 const unsigned numArgs,

489 const unsigned sizeArg,

490 const char *fn) const {

491

493 return;

494

495

497 ProgramStateRef trueState = nullptr, falseState = nullptr;

499 SVal argVal = C.getSVal(arg);

500

502 return;

503

504

506 (void) ReportZeroByteAllocation(C, falseState, arg, fn);

507 return;

508 }

509

510 assert(trueState);

511 if (trueState != state)

512 C.addTransition(trueState);

513}

514

515void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,

517 unsigned int nArgs = CE->getNumArgs();

518 if (nArgs != 2)

519 return;

520

522 ProgramStateRef trueState = nullptr, falseState = nullptr;

523

524 unsigned int i;

525 for (i = 0; i < nArgs; i++) {

527 SVal argVal = C.getSVal(arg);

529 if (i == 0)

530 continue;

531 else

532 return;

533 }

534

536 if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))

537 return;

538 else if (i == 0)

539 continue;

540 else

541 return;

542 }

543 }

544

545

546 assert(trueState);

547 if (trueState != state)

548 C.addTransition(trueState);

549}

550

551void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,

553 BasicAllocationCheck(C, CE, 1, 0, "malloc");

554}

555

556void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,

558 BasicAllocationCheck(C, CE, 2, 1, "realloc");

559}

560

561void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,

563 BasicAllocationCheck(C, CE, 2, 1, "reallocf");

564}

565

566void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,

568 BasicAllocationCheck(C, CE, 1, 0, "alloca");

569}

570

571void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(

574 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");

575}

576

577void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,

579 BasicAllocationCheck(C, CE, 1, 0, "valloc");

580}

581

582void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,

585 if (!FD || FD->getKind() != Decl::Function)

586 return;

587

588

589

591 if (isa_and_nonnull(NamespaceCtx))

592 return;

593

594 StringRef FName = C.getCalleeName(FD);

595 if (FName.empty())

596 return;

597

598 if (FName == "calloc")

599 CheckCallocZero(C, CE);

600

601 else if (FName == "malloc")

602 CheckMallocZero(C, CE);

603

604 else if (FName == "realloc")

605 CheckReallocZero(C, CE);

606

607 else if (FName == "reallocf")

608 CheckReallocfZero(C, CE);

609

610 else if (FName == "alloca" || FName == "__builtin_alloca")

611 CheckAllocaZero(C, CE);

612

613 else if (FName == "__builtin_alloca_with_align")

614 CheckAllocaWithAlignZero(C, CE);

615

616 else if (FName == "valloc")

617 CheckVallocZero(C, CE);

618}

619

620

621

622

623

624#define REGISTER_CHECKER(CHECKERNAME) \

625 void ento::register##CHECKERNAME(CheckerManager &mgr) { \

626 mgr.registerChecker(); \

627 } \

628 \

629 bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \

630 return true; \

631 }

632

static bool IsZeroByteAllocation(ProgramStateRef state, const SVal argVal, ProgramStateRef *trueState, ProgramStateRef *falseState)

@ OpenAt

The variant taking a directory file descriptor and a relative path: int openat(int fd,...

@ Open

The standard open() call: int open(const char *path, int oflag, ...);.

#define REGISTER_CHECKER(CHECKERNAME)

const TargetInfo & getTargetInfo() const

CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).

Expr * getArg(unsigned Arg)

getArg - Return the specified argument.

unsigned getNumArgs() const

getNumArgs - Return the number of actual arguments to this call.

DeclContext - This is used only as base class of specific decl types that can act as declaration cont...

DeclContext * getEnclosingNamespaceContext()

Retrieve the nearest enclosing namespace context.

This represents one expression.

Represents a function declaration or definition.

A (possibly-)qualified type.

A trivial tuple used to represent a source range.

SourceRange getSourceRange() const LLVM_READONLY

SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...

const llvm::Triple & getTriple() const

Returns the target triple of the primary target.

The top declaration context.

ASTContext & getASTContext() const

bool isIntegerType() const

isIntegerType() does not include complex integers (a GCC extension).

Preprocessor & getPreprocessor() override

BugReporter is a utility class for generating PathDiagnostics for analysis.

Represents an abstract call to a function or method along a particular path.

bool isUndef() const =delete

bool isUnknown() const =delete

MemRegion - The root abstract class for all memory regions.

LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const

SVal - This represents a symbolic expression, which can be either an L-value or an R-value.

bool isUnknownOrUndef() const

std::optional< T > getAs() const

Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.

T castAs() const

Convert to the specified SVal type, asserting that this SVal is of the desired type.

Defines the clang::TargetInfo interface.

bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})

Attempts to add visitors to track expression value back to its point of origin.

const char *const UnixAPI

DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB)

std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)

std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)

Try to parse the value of a defined preprocessor macro.

The JSON file list parser is used to communicate input to InstallAPI.

__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)