← Commit list

860b3d066

Denys Vlasenko
Denys Vlasenko
2024-10-07
SUCCESS Duration: 158s

Subject

ash: command -v CMD must skip (go to next path) when CMD exists, but is not executable

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

shell/ash.c+51 -3
@@ -491,6 +491,11 @@ struct globals_misc {
char **trap_ptr; /* used only by "trap hack" */
/* Rarely referenced stuff */
+
+ /* Cached supplementary group array (for testing executable'ity of files) */
+ int ngroups;
+ gid_t *group_array;
+
#if ENABLE_ASH_RANDOM_SUPPORT
random_t random_gen;
#endif
@@ -523,6 +528,8 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
#define may_have_traps (G_misc.may_have_traps )
#define trap (G_misc.trap )
#define trap_ptr (G_misc.trap_ptr )
+#define ngroups (G_misc.ngroups )
+#define group_array (G_misc.group_array)
#define random_gen (G_misc.random_gen )
#define backgndpid (G_misc.backgndpid )
#define INIT_G_misc() do { \
@@ -10171,6 +10178,7 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc,
#endif
#if ENABLE_ASH_TEST || BASH_TEST2
static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
+// TODO: pass &ngroups and &group_array addresses to test_main to use cached supplementary groups
#endif
#if ENABLE_ASH_SLEEP
static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); }
@@ -13782,6 +13790,43 @@ readcmdfile(char *name)
/* ============ find_command inplementation */
+static int test_exec(/*const char *fullname,*/ struct stat *statb)
+{
+ /*
+ * TODO: use faccessat(AT_FDCWD, fullname, X_OK, AT_EACCESS)
+ * instead: executability may depend on ACLs, capabilities
+ * and who knows what else, not just mode bits.
+ * (faccessat2 syscall was added to Linux in May 14 2020)
+ */
+ mode_t stmode;
+ uid_t euid;
+ enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH };
+
+ /* Do we already know with no extra syscalls? */
+ if (!S_ISREG(statb->st_mode))
+ return 0; /* not a regular file */
+ if ((statb->st_mode & ANY_IX) == 0)
+ return 0; /* no one can execute */
+ if ((statb->st_mode & ANY_IX) == ANY_IX)
+ return 1; /* anyone can execute */
+
+ /* Executability depends on our euid/egid/supplementary groups */
+ stmode = S_IXOTH;
+ euid = geteuid();
+//TODO: cache 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())
+ stmode = S_IXGRP;
+ else if (is_in_supplementary_groups(&ngroups, &group_array, statb->st_gid))
+ stmode = S_IXGRP;
+
+ return statb->st_mode & stmode;
+}
+
/*
* Resolve a command name. If you change this routine, you may have to
* change the shellexec routine as well.
@@ -13808,9 +13853,12 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
if (errno == EINTR)
continue;
#endif
+ absfail:
entry->cmdtype = CMDUNKNOWN;
return;
}
+ if (!test_exec(/*name,*/ &statb))
+ goto absfail;
}
entry->cmdtype = CMDNORMAL;
return;
@@ -13923,9 +13971,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
e = errno;
goto loop;
}
- e = EACCES; /* if we fail, this will be the error */
- if (!S_ISREG(statb.st_mode))
- continue;
if (lpathopt) { /* this is a %func directory */
stalloc(len);
/* NB: stalloc will return space pointed by fullname
@@ -13938,6 +13983,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
stunalloc(fullname);
goto success;
}
+ e = EACCES; /* if we fail, this will be the error */
+ if (!test_exec(/*fullname,*/ &statb))
+ continue;
TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
if (!updatetbl) {
entry->cmdtype = CMDNORMAL;