ash: cache more of uid/gid syscalls

Testcase:
setuidgid 1:1 strace ash -c 'test -x TODO; test -x TODO; echo $?'
should show that second "test -x" does not query ids again.

function                                             old     new   delta
ash_main                                            1236    1256     +20
get_cached_euid                                        -      19     +19
get_cached_egid                                        -      19     +19
test_main                                             56      72     +16
test_exec                                            119     135     +16
is_in_supplementary_groups                            52      57      +5
nexpr                                                718     702     -16
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 4/1 up/down: 95/-16)             Total: 79 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2024-10-07 07:28:44 +02:00
parent d26e958725
commit 96b0607302
4 changed files with 30 additions and 10 deletions

View file

@ -638,7 +638,7 @@ static int binop(void)
static int is_a_group_member(gid_t gid)
{
/* Short-circuit if possible, maybe saving a call to getgroups(). */
if (gid == getgid() || gid == getegid())
if (gid == get_cached_egid(&groupinfo->egid))
return 1;
return is_in_supplementary_groups(groupinfo, gid);
@ -656,15 +656,16 @@ static int test_st_mode(struct stat *st, int mode)
//TODO if (mode == X_OK) {
// /* Do we already know with no extra syscalls? */
// if (!S_ISREG(st->st_mode))
// return 0; /* not a regular file */
// //if (!S_ISREG(st->st_mode))
// // return 0; /* not a regular file */
// // ^^^ bash does not check this
// if ((st->st_mode & ANY_IX) == 0)
// return 0; /* no one can execute */
// if ((st->st_mode & ANY_IX) == ANY_IX)
// return 1; /* anyone can execute */
// }
euid = geteuid();
euid = get_cached_euid(&groupinfo->euid);
if (euid == 0) {
/* Root can read or write any file. */
if (mode != X_OK)
@ -1019,6 +1020,8 @@ int test_main(int argc, char **argv)
struct cached_groupinfo info;
int r;
info.euid = -1;
info.egid = -1;
info.ngroups = 0;
info.supplementary_array = NULL;
r = test_main2(&info, argc, argv);

View file

@ -1206,11 +1206,13 @@ gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC;
* getgroups() is cached in supplementary_array[], to make successive calls faster.
*/
struct cached_groupinfo {
//TODO? gid_t egid;
uid_t euid;
gid_t egid;
int ngroups;
gid_t *supplementary_array;
};
//TODO? int FAST_FUNC get_cached_egid(gid_t *egid);
uid_t FAST_FUNC get_cached_euid(uid_t *euid);
gid_t FAST_FUNC get_cached_egid(gid_t *egid);
int FAST_FUNC is_in_supplementary_groups(struct cached_groupinfo *groupinfo, gid_t gid);
#if ENABLE_FEATURE_UTMP

View file

@ -46,6 +46,20 @@ gid_t* FAST_FUNC bb_getgroups(int *ngroups, gid_t *group_array)
return group_array;
}
uid_t FAST_FUNC get_cached_euid(uid_t *euid)
{
if (*euid == (uid_t)-1)
*euid = geteuid();
return *euid;
}
gid_t FAST_FUNC get_cached_egid(gid_t *egid)
{
if (*egid == (gid_t)-1)
*egid = getegid();
return *egid;
}
/* Return non-zero if GID is in our supplementary group list. */
int FAST_FUNC is_in_supplementary_groups(struct cached_groupinfo *groupinfo, gid_t gid)
{

View file

@ -536,6 +536,8 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
curdir = nullstr; \
physdir = nullstr; \
trap_ptr = trap; \
groupinfo.euid = -1; \
groupinfo.egid = -1; \
} while (0)
@ -2319,7 +2321,7 @@ initvar(void)
#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
vps1.var_text = "PS1=\\w \\$ ";
#else
if (!geteuid())
if (!get_cached_euid(&groupinfo.euid));
vps1.var_text = "PS1=# ";
#endif
vp = varinit;
@ -13809,14 +13811,13 @@ static int test_exec(/*const char *fullname,*/ struct stat *statb)
/* Executability depends on our euid/egid/supplementary groups */
stmode = S_IXOTH;
euid = geteuid();
//TODO: cache euid?
euid = get_cached_euid(&groupinfo.euid);
if (euid == 0)
/* for root user, any X bit is good enough */
stmode = ANY_IX;
else if (statb->st_uid == euid)
stmode = S_IXUSR;
else if (statb->st_gid == getegid())
else if (statb->st_gid == get_cached_egid(&groupinfo.egid))
stmode = S_IXGRP;
else if (is_in_supplementary_groups(&groupinfo, statb->st_gid))
stmode = S_IXGRP;