29enum SetPrivilegeFunctionKind {
Irrelevant, Setuid, Setgid };
31class SetgidSetuidOrderChecker :
public Checker<check::PostCall, eval::Assume> {
32 const BugType BT{
this,
"Possible wrong order of privilege revocation"};
34 const CallDescription SetuidDesc{CDM::CLibrary, {
"setuid"}, 1};
35 const CallDescription SetgidDesc{CDM::CLibrary, {
"setgid"}, 1};
37 const CallDescription GetuidDesc{CDM::CLibrary, {
"getuid"}, 0};
38 const CallDescription GetgidDesc{CDM::CLibrary, {
"getgid"}, 0};
40 const CallDescriptionSet OtherSetPrivilegeDesc{
41 {CDM::CLibrary, {
"seteuid"}, 1}, {CDM::CLibrary, {
"setegid"}, 1},
42 {CDM::CLibrary, {
"setreuid"}, 2}, {CDM::CLibrary, {
"setregid"}, 2},
43 {CDM::CLibrary, {
"setresuid"}, 3}, {CDM::CLibrary, {
"setresgid"}, 3}};
46 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
48 bool Assumption)
const;
52 CheckerContext &
C)
const;
54 CheckerContext &
C)
const;
56 CheckerContext &
C)
const;
59 bool isFunctionCalledInArg(
const CallDescription &Desc,
60 const CallEvent &
Call)
const;
78void SetgidSetuidOrderChecker::checkPostCall(
const CallEvent &
Call,
82 processSetuid(State,
Call,
C);
84 processSetgid(State,
Call,
C);
86 processOther(State,
Call,
C);
92 bool Assumption)
const {
93 SValBuilder &SVB = State->getStateManager().getSValBuilder();
94 SymbolRef LastSetuidSym = State->get<LastSetuidCallSVal>();
103 auto FailComparison =
104 SVB.
evalBinOpNN(State, BO_NE, nonloc::SymbolVal(LastSetuidSym),
107 .
getAs<DefinedOrUnknownSVal>();
110 if (
auto IsFailBranch = State->assume(*FailComparison);
111 IsFailBranch.first && !IsFailBranch.second) {
114 State = State->set<LastSetPrivilegeCall>(
Irrelevant);
115 State = State->set<LastSetuidCallSVal>(
SymbolRef{});
121 const CallEvent &
Call,
122 CheckerContext &
C)
const {
123 bool IsSetuidWithGetuid = isFunctionCalledInArg(GetuidDesc,
Call);
124 if (State->get<LastSetPrivilegeCall>() != Setgid && IsSetuidWithGetuid) {
126 State = State->set<LastSetPrivilegeCall>(Setuid);
127 State = State->set<LastSetuidCallSVal>(RetSym);
128 const NoteTag *
Note =
C.getNoteTag([
this,
129 RetSym](PathSensitiveBugReport &BR) {
132 return "Call to 'setuid' found here that removes superuser privileges";
134 C.addTransition(State,
Note);
137 State = State->set<LastSetPrivilegeCall>(
Irrelevant);
138 State = State->set<LastSetuidCallSVal>(
SymbolRef{});
139 C.addTransition(State);
143 const CallEvent &
Call,
144 CheckerContext &
C)
const {
145 bool IsSetgidWithGetgid = isFunctionCalledInArg(GetgidDesc,
Call);
146 if (State->get<LastSetPrivilegeCall>() == Setuid) {
147 if (IsSetgidWithGetgid) {
148 State = State->set<LastSetPrivilegeCall>(
Irrelevant);
149 emitReport(State,
C);
152 State = State->set<LastSetPrivilegeCall>(
Irrelevant);
154 State = State->set<LastSetPrivilegeCall>(IsSetgidWithGetgid ? Setgid
157 State = State->set<LastSetuidCallSVal>(
SymbolRef{});
158 C.addTransition(State);
162 const CallEvent &
Call,
163 CheckerContext &
C)
const {
164 State = State->set<LastSetuidCallSVal>(
SymbolRef{});
165 State = State->set<LastSetPrivilegeCall>(
Irrelevant);
166 C.addTransition(State);
169bool SetgidSetuidOrderChecker::isFunctionCalledInArg(
170 const CallDescription &Desc,
const CallEvent &
Call)
const {
171 if (
const auto *CallInArg0 =
172 dyn_cast<CallExpr>(
Call.getArgExpr(0)->IgnoreParenImpCasts()))
178 CheckerContext &
C)
const {
179 if (ExplodedNode *N =
C.generateNonFatalErrorNode(State)) {
180 llvm::StringLiteral Msg =
181 "A 'setgid(getgid())' call following a 'setuid(getuid())' "
182 "call is likely to fail; probably the order of these "
183 "statements is wrong";
184 auto Report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
185 Report->markInteresting(State->get<LastSetuidCallSVal>());
186 C.emitReport(std::move(
Report));
190void ento::registerSetgidSetuidOrderChecker(CheckerManager &mgr) {
194bool ento::shouldRegisterSetgidSetuidOrderChecker(
const CheckerManager &mgr) {
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
const BugType & getBugType() const
bool contains(const CallEvent &Call) const
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
bool matchesAsWritten(const CallExpr &CE) const
Returns true if the CallExpr is a call to a function that matches the CallDescription.
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
bool isInteresting(SymbolRef sym) const
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0
Create a new value which represents a binary expression with two non- location operands.
QualType getConditionType() const
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
@ Irrelevant
We do not know anything about 'errno'.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
The JSON file list parser is used to communicate input to InstallAPI.