ChrootChecker.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //===-- ChrootChecker.cpp - chroot usage checks ---------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file defines chroot checker, which checks improper use of chroot.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  13. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  14. #include "clang/StaticAnalyzer/Core/Checker.h"
  15. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.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::PreCall> {
  37. // This bug refers to possibly break out of a chroot() jail.
  38. mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
  39. const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1};
  40. public:
  41. ChrootChecker() {}
  42. static void *getTag() {
  43. static int x;
  44. return &x;
  45. }
  46. bool evalCall(const CallEvent &Call, CheckerContext &C) const;
  47. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  48. private:
  49. void evalChroot(const CallEvent &Call, CheckerContext &C) const;
  50. void evalChdir(const CallEvent &Call, CheckerContext &C) const;
  51. };
  52. } // end anonymous namespace
  53. bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
  54. if (Chroot.matches(Call)) {
  55. evalChroot(Call, C);
  56. return true;
  57. }
  58. if (Chdir.matches(Call)) {
  59. evalChdir(Call, C);
  60. return true;
  61. }
  62. return false;
  63. }
  64. void ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const {
  65. ProgramStateRef state = C.getState();
  66. ProgramStateManager &Mgr = state->getStateManager();
  67. // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
  68. // the GDM.
  69. state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
  70. C.addTransition(state);
  71. }
  72. void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
  73. ProgramStateRef state = C.getState();
  74. ProgramStateManager &Mgr = state->getStateManager();
  75. // If there are no jail state in the GDM, just return.
  76. const void *k = state->FindGDM(ChrootChecker::getTag());
  77. if (!k)
  78. return;
  79. // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
  80. const Expr *ArgExpr = Call.getArgExpr(0);
  81. SVal ArgVal = C.getSVal(ArgExpr);
  82. if (const MemRegion *R = ArgVal.getAsRegion()) {
  83. R = R->StripCasts();
  84. if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
  85. const StringLiteral* Str = StrRegion->getStringLiteral();
  86. if (Str->getString() == "/")
  87. state = Mgr.addGDM(state, ChrootChecker::getTag(),
  88. (void*) JAIL_ENTERED);
  89. }
  90. }
  91. C.addTransition(state);
  92. }
  93. // Check the jail state before any function call except chroot and chdir().
  94. void ChrootChecker::checkPreCall(const CallEvent &Call,
  95. CheckerContext &C) const {
  96. // Ignore chroot and chdir.
  97. if (matchesAny(Call, Chroot, Chdir))
  98. return;
  99. // If jail state is ROOT_CHANGED, generate BugReport.
  100. void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
  101. if (k)
  102. if (isRootChanged((intptr_t) *k))
  103. if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
  104. if (!BT_BreakJail)
  105. BT_BreakJail.reset(new BuiltinBug(
  106. this, "Break out of jail", "No call of chdir(\"/\") immediately "
  107. "after chroot"));
  108. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  109. *BT_BreakJail, BT_BreakJail->getDescription(), N));
  110. }
  111. }
  112. void ento::registerChrootChecker(CheckerManager &mgr) {
  113. mgr.registerChecker<ChrootChecker>();
  114. }
  115. bool ento::shouldRegisterChrootChecker(const CheckerManager &mgr) {
  116. return true;
  117. }