Fix su_info cache bug

This commit is contained in:
topjohnwu 2019-09-04 11:04:59 -04:00
parent 8cf475f708
commit cdb53ca049

View File

@ -22,9 +22,6 @@
using namespace std; using namespace std;
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
static shared_ptr<su_info> cached; static shared_ptr<su_info> cached;
@ -86,53 +83,56 @@ static void database_check(const shared_ptr<su_info> &info) {
validate_manager(info->str[SU_MANAGER], uid / 100000, &info->mgr_st); validate_manager(info->str[SU_MANAGER], uid / 100000, &info->mgr_st);
} }
static shared_ptr<su_info> get_su_info(unsigned uid) { static void update_su_info(unsigned uid) {
shared_ptr<su_info> info; LOGD("su: request from uid=[%d]\n", uid);
// Get from cache or new instance RunFinally refresh([] {
LOCK_CACHE(); cached->refresh();
if (!cached || cached->uid != uid || !cached->is_fresh())
cached = make_shared<su_info>(uid);
info = cached;
info->refresh();
UNLOCK_CACHE();
LOGD("su: request from uid=[%d]\n", info->uid);
// Lock before the policy is determined
info->lock();
RunFinally unlock([&] {
info->unlock();
}); });
if (info->access.policy == QUERY) { // Get from cache or new instance
{
MutexGuard lock(cache_lock);
if (!cached || cached->uid != uid || !cached->is_fresh())
cached = make_shared<su_info>(uid);
else
return;
}
// Lock before the policy is determined
cached->lock();
RunFinally unlock([&] {
cached->unlock();
});
if (cached->access.policy == QUERY) {
// Not cached, get data from database // Not cached, get data from database
database_check(info); database_check(cached);
// If it's root or the manager, allow it silently // If it's root or the manager, allow it silently
if (info->uid == UID_ROOT || (info->uid % 100000) == (info->mgr_st.st_uid % 100000)) { if (cached->uid == UID_ROOT || (cached->uid % 100000) == (cached->mgr_st.st_uid % 100000)) {
info->access = SILENT_SU_ACCESS; cached->access = SILENT_SU_ACCESS;
return info; return;
} }
// Check su access settings // Check su access settings
switch (info->cfg[ROOT_ACCESS]) { switch (cached->cfg[ROOT_ACCESS]) {
case ROOT_ACCESS_DISABLED: case ROOT_ACCESS_DISABLED:
LOGW("Root access is disabled!\n"); LOGW("Root access is disabled!\n");
info->access = NO_SU_ACCESS; cached->access = NO_SU_ACCESS;
return info; return;
case ROOT_ACCESS_ADB_ONLY: case ROOT_ACCESS_ADB_ONLY:
if (info->uid != UID_SHELL) { if (cached->uid != UID_SHELL) {
LOGW("Root access limited to ADB only!\n"); LOGW("Root access limited to ADB only!\n");
info->access = NO_SU_ACCESS; cached->access = NO_SU_ACCESS;
return info; return;
} }
break; break;
case ROOT_ACCESS_APPS_ONLY: case ROOT_ACCESS_APPS_ONLY:
if (info->uid == UID_SHELL) { if (cached->uid == UID_SHELL) {
LOGW("Root access is disabled for ADB!\n"); LOGW("Root access is disabled for ADB!\n");
info->access = NO_SU_ACCESS; cached->access = NO_SU_ACCESS;
return info; return;
} }
break; break;
case ROOT_ACCESS_APPS_AND_ADB: case ROOT_ACCESS_APPS_AND_ADB:
@ -140,13 +140,13 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
break; break;
} }
if (info->access.policy != QUERY) if (cached->access.policy != QUERY)
return info; return;
// If still not determined, check if manager exists // If still not determined, check if manager exists
if (info->str[SU_MANAGER][0] == '\0') { if (cached->str[SU_MANAGER][0] == '\0') {
info->access = NO_SU_ACCESS; cached->access = NO_SU_ACCESS;
return info; return;
} }
} }
@ -155,19 +155,17 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
int sockfd = create_rand_socket(&addr); int sockfd = create_rand_socket(&addr);
// Connect manager // Connect manager
app_connect(addr.sun_path + 1, info); app_connect(addr.sun_path + 1, cached);
int fd = socket_accept(sockfd, 60); int fd = socket_accept(sockfd, 60);
if (fd < 0) { if (fd < 0) {
info->access.policy = DENY; cached->access.policy = DENY;
} else { } else {
socket_send_request(fd, info); socket_send_request(fd, cached);
int ret = read_int_be(fd); int ret = read_int_be(fd);
info->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret); cached->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret);
close(fd); close(fd);
} }
close(sockfd); close(sockfd);
return info;
} }
static void set_identity(unsigned uid) { static void set_identity(unsigned uid) {
@ -189,8 +187,9 @@ static void set_identity(unsigned uid) {
void su_daemon_handler(int client, struct ucred *credential) { void su_daemon_handler(int client, struct ucred *credential) {
LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client); LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client);
update_su_info(credential->uid);
su_context ctx = { su_context ctx = {
.info = get_su_info(credential->uid), .info = cached,
.req = su_request(true), .req = su_request(true),
.pid = credential->pid .pid = credential->pid
}; };