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/StringExtras.h"

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

27#include

28

29using namespace clang;

30using namespace ento;

31

41

45 if (MacroVal.has_value())

46 return MacroVal;

47

48

50 return {0x0200};

51 return MacroVal;

52}

53

54namespace {

55

56class UnixAPIMisuseChecker : public Checkercheck::PreCall {

57 const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};

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

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

63 const std::optional Val_O_CREAT;

64

66 EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,

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

69 std::nullopt) const;

70

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

73 const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;

74

75public:

76 UnixAPIMisuseChecker(const ASTContext &Ctx, const Preprocessor &PP)

78

79 void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,

80 BugReporter &BR) const;

81

82 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

83

84 void CheckOpen(CheckerContext &C, const CallEvent &Call) const;

85 void CheckOpenAt(CheckerContext &C, const CallEvent &Call) const;

86 void CheckGetDelimOrGetline(CheckerContext &C, const CallEvent &Call) const;

87 void CheckPthreadOnce(CheckerContext &C, const CallEvent &Call) const;

88

89 void CheckOpenVariant(CheckerContext &C, const CallEvent &Call,

91

92 void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg,

93 SourceRange SR) const;

94};

95

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

97public:

98 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;

99

100private:

101 const BugType BT_mallocZero{

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

104

105 void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;

106 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;

107 void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;

108 void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;

109 void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;

110 void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;

111 void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;

112

113 bool ReportZeroByteAllocation(CheckerContext &C,

115 const Expr *arg,

116 const char *fn_name) const;

117 void BasicAllocationCheck(CheckerContext &C,

118 const CallExpr *CE,

119 const unsigned numArgs,

120 const unsigned sizeArg,

121 const char *fn) const;

122};

123

124}

125

128 const StringRef PtrDescr,

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

130 const auto Ptr = PtrVal.getAs();

132 return State;

133

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

135 if (!PtrNotNull && PtrNull) {

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

137 auto R = std::make_unique(

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

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

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

142 }

143 return nullptr;

144 }

145

146 return PtrNotNull;

147}

148

149

150

151

152

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

154 CheckerContext &C) const {

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

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

157 return;

158

159

160

162 if (isa_and_nonnull(NamespaceCtx))

163 return;

164

165 StringRef FName = C.getCalleeName(FD);

166 if (FName.empty())

167 return;

168

169 if (FName == "open")

170 CheckOpen(C, Call);

171

172 else if (FName == "openat")

173 CheckOpenAt(C, Call);

174

175 else if (FName == "pthread_once")

176 CheckPthreadOnce(C, Call);

177

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

179 CheckGetDelimOrGetline(C, Call);

180}

181void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,

183 const char *Msg,

184 SourceRange SR) const {

185 ExplodedNode *N = C.generateErrorNode(State);

186 if (!N)

187 return;

188

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

190 Report->addRange(SR);

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

192}

193

194void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,

195 const CallEvent &Call) const {

197}

198

199void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,

200 const CallEvent &Call) const {

202}

203

204void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,

205 const CallEvent &Call,

207

208

209 unsigned int FlagsArgIndex;

210 const char *VariantName;

211 switch (Variant) {

213 FlagsArgIndex = 1;

214 VariantName = "open";

215 break;

217 FlagsArgIndex = 2;

218 VariantName = "openat";

219 break;

220 };

221

222

223 unsigned int MinArgCount = FlagsArgIndex + 1;

224

225

226 if (Call.getNumArgs() < MinArgCount)

227 return;

228

229

230

231 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;

232

233

234 unsigned int MaxArgCount = CreateModeArgIndex + 1;

235

237 if (Call.getNumArgs() == MaxArgCount) {

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

239 QualType QT = Arg->getType();

241 SmallString<256> SBuf;

242 llvm::raw_svector_ostream OS(SBuf);

243 OS << "The " << CreateModeArgIndex + 1

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

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

246

247 ReportOpenBug(C, state,

248 SBuf.c_str(),

250 return;

251 }

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

253 SmallString<256> SBuf;

254 llvm::raw_svector_ostream OS(SBuf);

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

256 << " arguments";

257

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

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

260 return;

261 }

262

263 if (!Val_O_CREAT.has_value()) {

264 return;

265 }

266

267

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

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

271

272

273 return;

274 }

275 NonLoc oflags = V.castAs();

276 NonLoc ocreateFlag = C.getSValBuilder()

277 .makeIntVal(Val_O_CREAT.value(), oflagsEx->getType())

278 .castAs();

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

280 oflags, ocreateFlag,

282 if (maskedFlagsUC.isUnknownOrUndef())

283 return;

284 DefinedSVal maskedFlags = maskedFlagsUC.castAs();

285

286

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

289

290

291

292 if (!(trueState && !falseState))

293 return;

294

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

296 SmallString<256> SBuf;

297 llvm::raw_svector_ostream OS(SBuf);

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

299 << CreateModeArgIndex + 1

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

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

302 ReportOpenBug(C, trueState,

303 SBuf.c_str(),

305 }

306}

307

308

309

310

311

312ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(

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

314 const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {

315 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =

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

317 "specified by the second parameter";

318 static constexpr llvm::StringLiteral SizeUndef =

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

320 "by the second parameter is undefined.";

321

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

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

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

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

329 }

330 };

331

332

333

334 const auto LinePtrValOpt = getPointeeVal(LinePtrPtrSVal, State);

335 if (!LinePtrValOpt)

336 return nullptr;

337

338 const auto LinePtrSVal = LinePtrValOpt->getAs();

339 const auto NSVal = getPointeeVal(SizePtrSVal, State);

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

341 return nullptr;

342

343 assert(LinePtrPtrExpr && SizePtrExpr);

344

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

346 if (LinePtrNotNull && !LinePtrNull) {

347

349 EmitBugReport(LinePtrNotNull, SizeUndef);

350 return nullptr;

351 }

352

353

354

355 auto NDefSVal = NSVal->getAs();

356 if (!NDefSVal)

357 return LinePtrNotNull;

358

359 auto &SVB = C.getSValBuilder();

360

361 const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();

362 if (!LinePtrRegion)

363 return LinePtrNotNull;

364

365 auto LineBufSize = getDynamicExtent(LinePtrNotNull, LinePtrRegion, SVB);

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

367 *NDefSVal, SVB.getConditionType())

368 .getAs();

369 if (!LineBufSizeGtN)

370 return LinePtrNotNull;

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

372 return LineBufSizeOk;

373

374 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);

375 return nullptr;

376 }

377 return State;

378}

379

380void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &C,

381 const CallEvent &Call) const {

382 if (Call.getNumArgs() < 2)

383 return;

384

386

387

388 SVal SizePtrSval = Call.getArgSVal(1);

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

390 if (!State)

391 return;

392

393

394 SVal LinePtrPtrSVal = Call.getArgSVal(0);

395 State =

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

397 if (!State)

398 return;

399

400 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,

401 Call.getArgExpr(0),

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

403 if (!State)

404 return;

405

406 C.addTransition(State);

407}

408

409

410

411

412

413void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,

414 const CallEvent &Call) const {

415

416

417

418

419 if (Call.getNumArgs() < 1)

420 return;

421

422

423

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

426 if (!R || !R->hasMemorySpace(state))

427 return;

428

429 ExplodedNode *N = C.generateErrorNode(state);

430 if (!N)

431 return;

432

433 SmallString<256> S;

434 llvm::raw_svector_ostream os(S);

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

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

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

438 else

439 os << " stack allocated memory";

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

441 "the control value is potentially dangerous.";

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

444

445 auto report =

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

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

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

449}

450

451

452

453

454

455

456

457

458

459

460

462 const SVal argVal,

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

467

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

469}

470

471

472

473

474bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(

475 CheckerContext &C,

477 const Expr *arg,

478 const char *fn_name) const {

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

480 if (!N)

481 return false;

482

483 SmallString<256> S;

484 llvm::raw_svector_ostream os(S);

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

486 auto report =

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

488

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

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

492

493 return true;

494}

495

496

497

498void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,

499 const CallExpr *CE,

500 const unsigned numArgs,

501 const unsigned sizeArg,

502 const char *fn) const {

503

505 return;

506

507

509 ProgramStateRef trueState = nullptr, falseState = nullptr;

510 const Expr *arg = CE->getArg(sizeArg);

511 SVal argVal = C.getSVal(arg);

512

514 return;

515

516

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

519 return;

520 }

521

522 assert(trueState);

523 if (trueState != state)

524 C.addTransition(trueState);

525}

526

527void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,

528 const CallExpr *CE) const {

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

530 if (nArgs != 2)

531 return;

532

534 ProgramStateRef trueState = nullptr, falseState = nullptr;

535

536 unsigned int i;

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

539 SVal argVal = C.getSVal(arg);

541 if (i == 0)

542 continue;

543 return;

544 }

545

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

548 return;

549 if (i == 0)

550 continue;

551 return;

552 }

553 }

554

555

556 assert(trueState);

557 if (trueState != state)

558 C.addTransition(trueState);

559}

560

561void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,

562 const CallExpr *CE) const {

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

564}

565

566void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,

567 const CallExpr *CE) const {

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

569}

570

571void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,

572 const CallExpr *CE) const {

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

574}

575

576void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,

577 const CallExpr *CE) const {

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

579}

580

581void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(

582 CheckerContext &C,

583 const CallExpr *CE) const {

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

585}

586

587void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,

588 const CallExpr *CE) const {

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

590}

591

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

593 CheckerContext &C) const {

594 const FunctionDecl *FD = C.getCalleeDecl(CE);

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

596 return;

597

598

599

601 if (isa_and_nonnull(NamespaceCtx))

602 return;

603

604 StringRef FName = C.getCalleeName(FD);

605 if (FName.empty())

606 return;

607

608 if (FName == "calloc")

609 CheckCallocZero(C, CE);

610

611 else if (FName == "malloc")

612 CheckMallocZero(C, CE);

613

614 else if (FName == "realloc")

615 CheckReallocZero(C, CE);

616

617 else if (FName == "reallocf")

618 CheckReallocfZero(C, CE);

619

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

621 CheckAllocaZero(C, CE);

622

623 else if (FName == "__builtin_alloca_with_align")

624 CheckAllocaWithAlignZero(C, CE);

625

626 else if (FName == "valloc")

627 CheckVallocZero(C, CE);

628}

629

630

631

632

633

634void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {

637}

638bool ento::shouldRegisterUnixAPIMisuseChecker(const CheckerManager &Mgr) {

639 return true;

640}

641

642void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {

644}

645bool ento::shouldRegisterUnixAPIPortabilityChecker(const CheckerManager &Mgr) {

646 return true;

647}

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

Definition UnixAPIChecker.cpp:461

OpenVariant

Definition UnixAPIChecker.cpp:32

@ OpenAt

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

Definition UnixAPIChecker.cpp:39

@ Open

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

Definition UnixAPIChecker.cpp:35

static std::optional< int > getCreateFlagValue(const ASTContext &Ctx, const Preprocessor &PP)

Definition UnixAPIChecker.cpp:42

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

const TargetInfo & getTargetInfo() const

Expr * getArg(unsigned Arg)

getArg - Return the specified argument.

unsigned getNumArgs() const

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

DeclContext * getEnclosingNamespaceContext()

Retrieve the nearest enclosing namespace context.

This represents one expression.

Engages in a tight little dance with the lexer to efficiently preprocess tokens.

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.

bool isPointerType() const

bool isIntegerType() const

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

ASTContext & getASTContext() const

CHECKER * registerChecker(AT &&...Args)

Register a single-part checker (derived from Checker): construct its singleton instance,...

const Preprocessor & getPreprocessor() const

Simple checker classes that implement one frontend (i.e.

bool isUndef() const =delete

bool isUnknown() const =delete

bool hasMemorySpace(ProgramStateRef State) 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

IntrusiveRefCntPtr< const ProgramState > ProgramStateRef

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

@ OS

Indicates that the tracking object is a descendant of a referenced-counted OSObject,...

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.

bool isa(CodeGen::Address addr)

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