diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2024-10-07 05:46:31 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-10-07 05:46:31 +0200 |
commit | 860b3d066f6aaa12dfa0cd2351559e05288cf9b5 (patch) | |
tree | 2cfc5bb917507ae6ff3086a66acb8b6e89d1f67c | |
parent | 748b1681549067f2e27ab2b9102ef9352cfa8a4c (diff) | |
download | busybox-w32-860b3d066f6aaa12dfa0cd2351559e05288cf9b5.tar.gz busybox-w32-860b3d066f6aaa12dfa0cd2351559e05288cf9b5.tar.bz2 busybox-w32-860b3d066f6aaa12dfa0cd2351559e05288cf9b5.zip |
ash: command -v CMD must skip (go to next path) when CMD exists, but is not executable
Upstream commit:
Date: Fri, 5 Apr 2024 17:55:46 +0800
exec: Check executable bit when searching path
Andrej Shadura <andrew.shadura@collabora.co.uk> wrote:
...
> https://bugs.debian.org/874264
> -------- Forwarded Message --------
> Subject: dash: 'command -v' mistakenly returns a shell script whose
> executable is not set
> Date: Mon, 04 Sep 2017 10:45:48 -0400
> From: Norman Ramsey <nr@cs.tufts.edu>
> To: Debian Bug Tracking System <submit@bugs.debian.org>
...
> I tracked a build bug in s-nail to a problem with dash. Symptom:
> building s-nail tries to run /home/nr/bin/clang, a script whose
> executable bit is not set. We tracked the problem to the result of
> running `command -v clang` with /bin/sh:
...
This is inherited from NetBSD. There is even a commented-out
block of code that tried to fix this.
Anyway, we now have faccessat so we can simply use it.
function old new delta
test_exec - 125 +125
find_command 911 918 +7
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/0 up/down: 132/0) Total: 132 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash.c | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/shell/ash.c b/shell/ash.c index 8e029765d..984a71f07 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -491,6 +491,11 @@ struct globals_misc { | |||
491 | char **trap_ptr; /* used only by "trap hack" */ | 491 | char **trap_ptr; /* used only by "trap hack" */ |
492 | 492 | ||
493 | /* Rarely referenced stuff */ | 493 | /* Rarely referenced stuff */ |
494 | |||
495 | /* Cached supplementary group array (for testing executable'ity of files) */ | ||
496 | int ngroups; | ||
497 | gid_t *group_array; | ||
498 | |||
494 | #if ENABLE_ASH_RANDOM_SUPPORT | 499 | #if ENABLE_ASH_RANDOM_SUPPORT |
495 | random_t random_gen; | 500 | random_t random_gen; |
496 | #endif | 501 | #endif |
@@ -523,6 +528,8 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
523 | #define may_have_traps (G_misc.may_have_traps ) | 528 | #define may_have_traps (G_misc.may_have_traps ) |
524 | #define trap (G_misc.trap ) | 529 | #define trap (G_misc.trap ) |
525 | #define trap_ptr (G_misc.trap_ptr ) | 530 | #define trap_ptr (G_misc.trap_ptr ) |
531 | #define ngroups (G_misc.ngroups ) | ||
532 | #define group_array (G_misc.group_array) | ||
526 | #define random_gen (G_misc.random_gen ) | 533 | #define random_gen (G_misc.random_gen ) |
527 | #define backgndpid (G_misc.backgndpid ) | 534 | #define backgndpid (G_misc.backgndpid ) |
528 | #define INIT_G_misc() do { \ | 535 | #define INIT_G_misc() do { \ |
@@ -10171,6 +10178,7 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, | |||
10171 | #endif | 10178 | #endif |
10172 | #if ENABLE_ASH_TEST || BASH_TEST2 | 10179 | #if ENABLE_ASH_TEST || BASH_TEST2 |
10173 | static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } | 10180 | static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } |
10181 | // TODO: pass &ngroups and &group_array addresses to test_main to use cached supplementary groups | ||
10174 | #endif | 10182 | #endif |
10175 | #if ENABLE_ASH_SLEEP | 10183 | #if ENABLE_ASH_SLEEP |
10176 | static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } | 10184 | static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } |
@@ -13782,6 +13790,43 @@ readcmdfile(char *name) | |||
13782 | 13790 | ||
13783 | /* ============ find_command inplementation */ | 13791 | /* ============ find_command inplementation */ |
13784 | 13792 | ||
13793 | static int test_exec(/*const char *fullname,*/ struct stat *statb) | ||
13794 | { | ||
13795 | /* | ||
13796 | * TODO: use faccessat(AT_FDCWD, fullname, X_OK, AT_EACCESS) | ||
13797 | * instead: executability may depend on ACLs, capabilities | ||
13798 | * and who knows what else, not just mode bits. | ||
13799 | * (faccessat2 syscall was added to Linux in May 14 2020) | ||
13800 | */ | ||
13801 | mode_t stmode; | ||
13802 | uid_t euid; | ||
13803 | enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH }; | ||
13804 | |||
13805 | /* Do we already know with no extra syscalls? */ | ||
13806 | if (!S_ISREG(statb->st_mode)) | ||
13807 | return 0; /* not a regular file */ | ||
13808 | if ((statb->st_mode & ANY_IX) == 0) | ||
13809 | return 0; /* no one can execute */ | ||
13810 | if ((statb->st_mode & ANY_IX) == ANY_IX) | ||
13811 | return 1; /* anyone can execute */ | ||
13812 | |||
13813 | /* Executability depends on our euid/egid/supplementary groups */ | ||
13814 | stmode = S_IXOTH; | ||
13815 | euid = geteuid(); | ||
13816 | //TODO: cache euid? | ||
13817 | if (euid == 0) | ||
13818 | /* for root user, any X bit is good enough */ | ||
13819 | stmode = ANY_IX; | ||
13820 | else if (statb->st_uid == euid) | ||
13821 | stmode = S_IXUSR; | ||
13822 | else if (statb->st_gid == getegid()) | ||
13823 | stmode = S_IXGRP; | ||
13824 | else if (is_in_supplementary_groups(&ngroups, &group_array, statb->st_gid)) | ||
13825 | stmode = S_IXGRP; | ||
13826 | |||
13827 | return statb->st_mode & stmode; | ||
13828 | } | ||
13829 | |||
13785 | /* | 13830 | /* |
13786 | * Resolve a command name. If you change this routine, you may have to | 13831 | * Resolve a command name. If you change this routine, you may have to |
13787 | * change the shellexec routine as well. | 13832 | * change the shellexec routine as well. |
@@ -13808,9 +13853,12 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13808 | if (errno == EINTR) | 13853 | if (errno == EINTR) |
13809 | continue; | 13854 | continue; |
13810 | #endif | 13855 | #endif |
13856 | absfail: | ||
13811 | entry->cmdtype = CMDUNKNOWN; | 13857 | entry->cmdtype = CMDUNKNOWN; |
13812 | return; | 13858 | return; |
13813 | } | 13859 | } |
13860 | if (!test_exec(/*name,*/ &statb)) | ||
13861 | goto absfail; | ||
13814 | } | 13862 | } |
13815 | entry->cmdtype = CMDNORMAL; | 13863 | entry->cmdtype = CMDNORMAL; |
13816 | return; | 13864 | return; |
@@ -13923,9 +13971,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13923 | e = errno; | 13971 | e = errno; |
13924 | goto loop; | 13972 | goto loop; |
13925 | } | 13973 | } |
13926 | e = EACCES; /* if we fail, this will be the error */ | ||
13927 | if (!S_ISREG(statb.st_mode)) | ||
13928 | continue; | ||
13929 | if (lpathopt) { /* this is a %func directory */ | 13974 | if (lpathopt) { /* this is a %func directory */ |
13930 | stalloc(len); | 13975 | stalloc(len); |
13931 | /* NB: stalloc will return space pointed by fullname | 13976 | /* NB: stalloc will return space pointed by fullname |
@@ -13938,6 +13983,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13938 | stunalloc(fullname); | 13983 | stunalloc(fullname); |
13939 | goto success; | 13984 | goto success; |
13940 | } | 13985 | } |
13986 | e = EACCES; /* if we fail, this will be the error */ | ||
13987 | if (!test_exec(/*fullname,*/ &statb)) | ||
13988 | continue; | ||
13941 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); | 13989 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); |
13942 | if (!updatetbl) { | 13990 | if (!updatetbl) { |
13943 | entry->cmdtype = CMDNORMAL; | 13991 | entry->cmdtype = CMDNORMAL; |