ChrootChecker.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. //===- Chrootchecker.cpp -------- Basic security checks ----------*- C++ -*-==//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This file defines chroot checker, which checks improper use of chroot.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "ClangSACheckers.h"
  14. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
  21. #include "llvm/ADT/ImmutableMap.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. // enum value that represent the jail state
  26. enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };
  27. bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
  28. //bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
  29. // This checker checks improper use of chroot.
  30. // The state transition:
  31. // NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED
  32. // | |
  33. // ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)--
  34. // | |
  35. // bug<--foo()-- JAIL_ENTERED<--foo()--
  36. class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > {
  37. mutable IdentifierInfo *II_chroot, *II_chdir;
  38. // This bug refers to possibly break out of a chroot() jail.
  39. mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
  40. public:
  41. ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {}
  42. static void *getTag() {
  43. static int x;
  44. return &x;
  45. }
  46. bool evalCall(const CallExpr *CE, CheckerContext &C) const;
  47. void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
  48. private:
  49. void Chroot(CheckerContext &C, const CallExpr *CE) const;
  50. void Chdir(CheckerContext &C, const CallExpr *CE) const;
  51. };
  52. } // end anonymous namespace
  53. bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
  54. const FunctionDecl *FD = C.getCalleeDecl(CE);
  55. if (!FD)
  56. return false;
  57. ASTContext &Ctx = C.getASTContext();
  58. if (!II_chroot)
  59. II_chroot = &Ctx.Idents.get("chroot");
  60. if (!II_chdir)
  61. II_chdir = &Ctx.Idents.get("chdir");
  62. if (FD->getIdentifier() == II_chroot) {
  63. Chroot(C, CE);
  64. return true;
  65. }
  66. if (FD->getIdentifier() == II_chdir) {
  67. Chdir(C, CE);
  68. return true;
  69. }
  70. return false;
  71. }
  72. void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
  73. ProgramStateRef state = C.getState();
  74. ProgramStateManager &Mgr = state->getStateManager();
  75. // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
  76. // the GDM.
  77. state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
  78. C.addTransition(state);
  79. }
  80. void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
  81. ProgramStateRef state = C.getState();
  82. ProgramStateManager &Mgr = state->getStateManager();
  83. // If there are no jail state in the GDM, just return.
  84. const void *k = state->FindGDM(ChrootChecker::getTag());
  85. if (!k)
  86. return;
  87. // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
  88. const Expr *ArgExpr = CE->getArg(0);
  89. SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
  90. if (const MemRegion *R = ArgVal.getAsRegion()) {
  91. R = R->StripCasts();
  92. if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
  93. const StringLiteral* Str = StrRegion->getStringLiteral();
  94. if (Str->getString() == "/")
  95. state = Mgr.addGDM(state, ChrootChecker::getTag(),
  96. (void*) JAIL_ENTERED);
  97. }
  98. }
  99. C.addTransition(state);
  100. }
  101. // Check the jail state before any function call except chroot and chdir().
  102. void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
  103. const FunctionDecl *FD = C.getCalleeDecl(CE);
  104. if (!FD)
  105. return;
  106. ASTContext &Ctx = C.getASTContext();
  107. if (!II_chroot)
  108. II_chroot = &Ctx.Idents.get("chroot");
  109. if (!II_chdir)
  110. II_chdir = &Ctx.Idents.get("chdir");
  111. // Ingnore chroot and chdir.
  112. if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
  113. return;
  114. // If jail state is ROOT_CHANGED, generate BugReport.
  115. void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
  116. if (k)
  117. if (isRootChanged((intptr_t) *k))
  118. if (ExplodedNode *N = C.addTransition()) {
  119. if (!BT_BreakJail)
  120. BT_BreakJail.reset(new BuiltinBug(
  121. this, "Break out of jail", "No call of chdir(\"/\") immediately "
  122. "after chroot"));
  123. C.emitReport(llvm::make_unique<BugReport>(
  124. *BT_BreakJail, BT_BreakJail->getDescription(), N));
  125. }
  126. return;
  127. }
  128. void ento::registerChrootChecker(CheckerManager &mgr) {
  129. mgr.registerChecker<ChrootChecker>();
  130. }