// RUN: %clang_analyze_cc1 -analyzer-checker=core,security.SetgidSetuidOrder -verify %s
typedef int uid_t;
typedef int gid_t;
int setuid(uid_t);
int setgid(gid_t);
int seteuid(uid_t);
int setegid(gid_t);
int setreuid(uid_t, uid_t);
int setregid(gid_t, gid_t);
int setresuid(uid_t, uid_t, uid_t);
int setresgid(gid_t, gid_t, gid_t);
uid_t getuid();
gid_t getgid();
void correct_order() {
// A correct revocation sequence starts here.
if (setgid(getgid()) == -1)
return;
if (setuid(getuid()) == -1)
return;
// No warning for the following setgid statement.
// The previous setgid and setuid calls are a correct privilege revocation
// sequence. The checker does not care about the following statements (except
// if a wrong setuid-setgid sequence follows again).
if (setgid(getgid()) == -1)
return;
}
void incorrect_after_correct() {
if (setgid(getgid()) == -1)
return;
if (setuid(getuid()) == -1)
return;
// Incorrect sequence starts here.
if (setuid(getuid()) == -1)
return;
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
}
void incorrect_order() {
if (setuid(getuid()) == -1)
return;
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
if (setgid(getgid()) == -1)
return;
}
void warn_at_second_time() {
if (setuid(getuid()) == -1)
return;
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
if (setuid(getuid()) == -1)
return;
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
}
uid_t f_uid();
gid_t f_gid();
void setuid_other() {
if (setuid(f_uid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setgid_other() {
if (setuid(getuid()) == -1)
return;
if (setgid(f_gid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setuid_other_between() {
if (setuid(getuid()) == -1)
return;
if (setuid(f_uid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setgid_with_getuid() {
if (setuid(getuid()) == -1)
return;
// add a clang-tidy check for this case?
if (setgid(getuid()) == -1)
return;
}
void setuid_with_getgid() {
// add a clang-tidy check for this case?
if (setuid(getgid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
int f_setuid() {
return setuid(getuid());
}
int f_setgid() {
return setgid(getgid()); // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
}
void function_calls() {
if (f_setuid() == -1)
return;
if (f_setgid() == -1)
return;
}
void seteuid_between() {
if (setuid(getuid()) == -1)
return;
if (seteuid(getuid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setegid_between() {
if (setuid(getuid()) == -1)
return;
if (setegid(getgid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setreuid_between() {
if (setuid(getuid()) == -1)
return;
if (setreuid(getuid(), getuid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setregid_between() {
if (setuid(getuid()) == -1)
return;
if (setregid(getgid(), getgid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setresuid_between() {
if (setuid(getuid()) == -1)
return;
if (setresuid(getuid(), getuid(), getuid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void setresgid_between() {
if (setuid(getuid()) == -1)
return;
if (setresgid(getgid(), getgid(), getgid()) == -1)
return;
if (setgid(getgid()) == -1)
return;
}
void getgid_getuid_between() {
if (setuid(getuid()) == -1)
return;
(void)getgid();
(void)getuid();
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
}
void stored_getgid_getuid() {
// possible future improvement: detect this case
uid_t u = getuid();
gid_t g = getgid();
if (setuid(u) == -1)
return;
if (setgid(g) == -1) // no warning
return;
}
void f_extern();
void other_unknown_function_between() {
if (setuid(getuid()) == -1)
return;
f_extern();
if (setgid(getgid()) == -1) // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
}
void setuid_error_case() {
if (setuid(getuid()) == -1) {
// No warning if we know that the first setuid call has failed.
(void)setgid(getgid());
return;
}
(void)setgid(getgid()); // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
}
void setuid_success_case() {
if (setuid(getuid()) == 0) {
if (setgid(getgid()) == 0) { // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
}
}
}
void incorrect_order_compare_zero() {
if (setuid(getuid()) != 0)
return;
(void)setgid(getgid()); // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
}
void setuid_error_case_compare_zero() {
if (setuid(getuid()) != 0) {
// No warning if we know that the first setuid call has failed.
(void)setgid(getgid());
return;
}
}
void incorrect_order_compare_other() {
if (setuid(getuid()) == -2) {
// This is a case for improvement:
// The checker does not recognize that this is an invalid error check,
// but this is really another type of bug not related to this checker.
(void)setgid(getgid()); // warning should appear here
return;
}
if (setgid(getgid()) == -2) { // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
return;
}
}
const int FAIL = -1;
void incorrect_order_compare_var() {
if (setuid(getuid()) == FAIL)
return;
(void)setgid(getgid()); // expected-warning{{A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail}}
}