// 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}} }