Simpler su_info caching system

This commit is contained in:
topjohnwu 2018-12-26 11:56:49 +08:00
parent 23f8f35098
commit 523e66294b
2 changed files with 32 additions and 39 deletions

View File

@ -30,12 +30,15 @@ public:
/* These should be guarded with global cache lock */ /* These should be guarded with global cache lock */
int ref; int ref;
int life; time_t timestamp;
su_info(unsigned uid); su_info(unsigned uid);
~su_info(); ~su_info();
void lock(); void lock();
void unlock(); void unlock();
bool isFresh();
void newRef();
void deRef();
private: private:
pthread_mutex_t _lock; /* Internal lock */ pthread_mutex_t _lock; /* Internal lock */

View File

@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <pwd.h> #include <pwd.h>
#include <time.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -17,8 +18,6 @@
#include "pts.h" #include "pts.h"
#include "selinux.h" #include "selinux.h"
#define TIMEOUT 3
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock) #define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock) #define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
@ -27,7 +26,7 @@ static su_info *cache;
su_info::su_info(unsigned uid) : su_info::su_info(unsigned uid) :
uid(uid), access(DEFAULT_SU_ACCESS), _lock(PTHREAD_MUTEX_INITIALIZER), uid(uid), access(DEFAULT_SU_ACCESS), _lock(PTHREAD_MUTEX_INITIALIZER),
count(0), ref(0), life(0), mgr_st({}) {} count(0), ref(0), timestamp(0), mgr_st({}) {}
su_info::~su_info() { su_info::~su_info() {
pthread_mutex_destroy(&_lock); pthread_mutex_destroy(&_lock);
@ -41,21 +40,24 @@ void su_info::unlock() {
pthread_mutex_unlock(&_lock); pthread_mutex_unlock(&_lock);
} }
static void *info_collector(void *node) { bool su_info::isFresh() {
su_info *info = (su_info *) node; return time(nullptr) - timestamp < 3; /* 3 seconds */
while (1) { }
sleep(1);
if (info->life) { void su_info::newRef() {
LOCK_CACHE(); timestamp = time(nullptr);
if (--info->life == 0 && cache && info->uid == cache->uid) ++ref;
cache = nullptr; }
UNLOCK_CACHE();
} void su_info::deRef() {
if (!info->life && !info->ref) { LOCK_CACHE();
delete info; --ref;
return nullptr; if (ref == 0 && !isFresh()) {
} if (cache == this)
cache = nullptr;
delete this;
} }
UNLOCK_CACHE();
} }
static void database_check(su_info *info) { static void database_check(su_info *info) {
@ -88,30 +90,18 @@ static void database_check(su_info *info) {
} }
static struct su_info *get_su_info(unsigned uid) { static struct su_info *get_su_info(unsigned uid) {
su_info *info; su_info *info = nullptr;
bool cache_miss = false;
// Get from cache or new instance
LOCK_CACHE(); LOCK_CACHE();
if (cache && cache->uid == uid && cache->isFresh()) {
if (cache && cache->uid == uid) {
info = cache; info = cache;
} else { } else {
cache_miss = true; if (cache && cache->ref == 0)
info = new su_info(uid); delete cache;
cache = info; cache = info = new su_info(uid);
} }
info->newRef();
// Update the cache status
info->life = TIMEOUT;
++info->ref;
// Start a thread to maintain the cache
if (cache_miss) {
pthread_t thread;
xpthread_create(&thread, nullptr, info_collector, info);
pthread_detach(thread);
}
UNLOCK_CACHE(); UNLOCK_CACHE();
LOGD("su: request from uid=[%d] (#%d)\n", info->uid, ++info->count); LOGD("su: request from uid=[%d] (#%d)\n", info->uid, ++info->count);
@ -209,6 +199,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
// Fail fast // Fail fast
if (info->access.policy == DENY && info->str[SU_MANAGER][0] == '\0') { if (info->access.policy == DENY && info->str[SU_MANAGER][0] == '\0') {
LOGD("su: fast deny\n"); LOGD("su: fast deny\n");
info->deRef();
write_int(client, DENY); write_int(client, DENY);
close(client); close(client);
return; return;
@ -221,8 +212,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
*/ */
int child = xfork(); int child = xfork();
if (child) { if (child) {
// Decrement reference count info->deRef();
--info->ref;
// Wait result // Wait result
LOGD("su: waiting child pid=[%d]\n", child); LOGD("su: waiting child pid=[%d]\n", child);