llvm/clang/test/Analysis/setgid-setuid-order.c

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