From cd4142f7c3757f3337c20a971b98c673e68a66a1 Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:32:00 +0000 Subject: [PATCH 1/8] whereis: fix out of boundary read If whereis encounters a short file name then an out of boundary read can occur. Signed-off-by: Samanta Navarro --- misc-utils/whereis.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index 7a28a2c35..bc53a984a 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -398,13 +398,13 @@ static int filename_equal(const char *cp, const char *dp) if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2)) return 1; - if (!strcmp(dp + i - 2, ".Z")) + if (i > 1 && !strcmp(dp + i - 2, ".Z")) i -= 2; - else if (!strcmp(dp + i - 3, ".gz")) + else if (i > 2 && !strcmp(dp + i - 3, ".gz")) i -= 3; - else if (!strcmp(dp + i - 3, ".xz")) + else if (i > 2 && !strcmp(dp + i - 3, ".xz")) i -= 3; - else if (!strcmp(dp + i - 4, ".bz2")) + else if (i > 3 && !strcmp(dp + i - 4, ".bz2")) i -= 4; while (*cp && *dp && *cp == *dp) cp++, dp++, i--; From 6065250a4d0e20aac3dcb19ec6aa4f1ba0811fef Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:33:00 +0000 Subject: [PATCH 2/8] whereis: support zst compressed man pages Add zst as extension for manual pages. Current version of man-db supports zst extension out of the box. Signed-off-by: Samanta Navarro --- misc-utils/whereis.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index bc53a984a..0247dbec7 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -406,6 +406,8 @@ static int filename_equal(const char *cp, const char *dp) i -= 3; else if (i > 3 && !strcmp(dp + i - 4, ".bz2")) i -= 4; + else if (i > 3 && !strcmp(dp + i - 4, ".zst")) + i -= 4; while (*cp && *dp && *cp == *dp) cp++, dp++, i--; if (*cp == 0 && *dp == 0) From 74b7c01fa6c009d00c3ef894d6c6a2a9dd37c41c Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:34:00 +0000 Subject: [PATCH 3/8] whereis: add lib32 directories These directories are sometimes used by Linux distributions. Signed-off-by: Samanta Navarro --- misc-utils/whereis.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index 0247dbec7..49c7b410f 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -107,10 +107,12 @@ static const char *bindirs[] = { "/usr/local/lib/" MULTIARCHTRIPLET, #endif "/usr/lib", + "/usr/lib32", "/usr/lib64", "/etc", "/usr/etc", "/lib", + "/lib32", "/lib64", "/usr/games", "/usr/games/bin", From b7fc158a2eb5433f178337773b4f9fd7805555d8 Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:35:00 +0000 Subject: [PATCH 4/8] whereis: add --disable-whereis to configure Allow a build of util-linux without whereis. Signed-off-by: Samanta Navarro --- configure.ac | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9d4785c95..a99437697 100644 --- a/configure.ac +++ b/configure.ac @@ -1651,7 +1651,11 @@ if test "x$matriplet" != "x"; then AC_DEFINE_UNQUOTED([MULTIARCHTRIPLET], ["$matriplet"], ["Multi-arch triplet for whereis library search path"]) fi -UL_BUILD_INIT([whereis], [yes]) +AC_ARG_ENABLE([whereis], + AS_HELP_STRING([--disable-whereis], [do not build whereis]), + [], [UL_DEFAULT_ENABLE([whereis], [check])] +) +UL_BUILD_INIT([whereis]) AM_CONDITIONAL([BUILD_WHEREIS], [test "x$build_whereis" = xyes]) UL_BUILD_INIT([getopt], [yes]) From d95ee9fd759762ce11b292d5d4484dd62888645e Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:36:00 +0000 Subject: [PATCH 5/8] whereis: do not ignore trailing numbers The commands diff and diff3 are so distinct that their manual pages should not be mixed in whereis output. Theoretically this works for commands and binaries with links to each other, e.g. gpg and gpg2, but if gpg is version 1 and gpg2 is version 2 then manual pages do not match either. Also the while loop does not decrement "i" while incrementing "dp". The effect of this is that the output of whereis depends on manual pages being compressed or not. The easiest solution is to not ignore trailing numbers. Signed-off-by: Samanta Navarro --- misc-utils/whereis.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index 49c7b410f..e3e480bdc 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -414,8 +414,6 @@ static int filename_equal(const char *cp, const char *dp) cp++, dp++, i--; if (*cp == 0 && *dp == 0) return 1; - while (isdigit(*dp)) - dp++; if (*cp == 0 && *dp++ == '.') { --i; while (i > 0 && *dp) From 117ddbeedc02bfc93f6776ca27c3a5501bd718e5 Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:37:00 +0000 Subject: [PATCH 6/8] whereis: do not strip suffixes The whereis implementations of FreeBSD, macOS, NetBSD, and OpenBSD do not strip suffixes. Although whereis is not a POSIX tool and has no shared standard, even its manual page indicates that the supplied names are command names. Commands do not have a suffix on Linux systems. Stripping suffixes actually leads to issues with tools like fsck.ext4, since fsck.ext4 is not the same tool as fsck and definitely not the same tool as fsck.minix. Signed-off-by: Samanta Navarro --- misc-utils/whereis.1 | 6 +----- misc-utils/whereis.c | 3 --- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/misc-utils/whereis.1 b/misc-utils/whereis.1 index 08e351cec..2ecc70f21 100644 --- a/misc-utils/whereis.1 +++ b/misc-utils/whereis.1 @@ -42,11 +42,7 @@ whereis \- locate the binary, source, and manual page files for a command .SH DESCRIPTION .B whereis locates the binary, source and manual files for the specified command names. -The supplied names are first stripped of leading pathname components and any -(single) trailing extension of the form -.BI . ext -(for example: -.BR .c ) +The supplied names are first stripped of leading pathname components. Prefixes of .B s. resulting from use of source code control are also dealt with. diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index e3e480bdc..d94e25a58 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -469,9 +469,6 @@ static void lookup(const char *pattern, struct wh_dirlist *ls, int want) want & BIN_DIR ? "bin" : "", want & MAN_DIR ? "man" : "", want & SRC_DIR ? "src" : "")); - p = strrchr(patbuf, '.'); - if (p) - *p = '\0'; if (!uflag) /* if -u not specified then we always print the pattern */ From 653f672ab61346fea2ec10d3706f0e3e9e8f51de Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:38:00 +0000 Subject: [PATCH 7/8] whereis: filter bin, man and src differently Consider "s." prefixes for source code files only (even though I do not know which VCS does that), compression suffixes for manual pages and strict matching for executables. Calling "whereis python3" is kind of okay to return python3.8 next to python3, but python3.8-config is not the same tool as python3. Signed-off-by: Samanta Navarro --- misc-utils/whereis.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index d94e25a58..db092ce60 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -392,29 +392,32 @@ static void free_dirlist(struct wh_dirlist **ls0, int type) } -static int filename_equal(const char *cp, const char *dp) +static int filename_equal(const char *cp, const char *dp, int type) { int i = strlen(dp); DBG(SEARCH, ul_debug("compare '%s' and '%s'", cp, dp)); - if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2)) + if (type & SRC_DIR && + dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2, type)) return 1; - if (i > 1 && !strcmp(dp + i - 2, ".Z")) - i -= 2; - else if (i > 2 && !strcmp(dp + i - 3, ".gz")) - i -= 3; - else if (i > 2 && !strcmp(dp + i - 3, ".xz")) - i -= 3; - else if (i > 3 && !strcmp(dp + i - 4, ".bz2")) - i -= 4; - else if (i > 3 && !strcmp(dp + i - 4, ".zst")) - i -= 4; + if (type & MAN_DIR) { + if (i > 1 && !strcmp(dp + i - 2, ".Z")) + i -= 2; + else if (i > 2 && !strcmp(dp + i - 3, ".gz")) + i -= 3; + else if (i > 2 && !strcmp(dp + i - 3, ".xz")) + i -= 3; + else if (i > 3 && !strcmp(dp + i - 4, ".bz2")) + i -= 4; + else if (i > 3 && !strcmp(dp + i - 4, ".zst")) + i -= 4; + } while (*cp && *dp && *cp == *dp) cp++, dp++, i--; if (*cp == 0 && *dp == 0) return 1; - if (*cp == 0 && *dp++ == '.') { + if (!(type & BIN_DIR) && *cp == 0 && *dp++ == '.') { --i; while (i > 0 && *dp) if (--i, *dp++ == '.') @@ -424,7 +427,8 @@ static int filename_equal(const char *cp, const char *dp) return 0; } -static void findin(const char *dir, const char *pattern, int *count, char **wait) +static void findin(const char *dir, const char *pattern, int *count, + char **wait, int type) { DIR *dirp; struct dirent *dp; @@ -436,7 +440,7 @@ static void findin(const char *dir, const char *pattern, int *count, char **wait DBG(SEARCH, ul_debug("find '%s' in '%s'", pattern, dir)); while ((dp = readdir(dirp)) != NULL) { - if (!filename_equal(pattern, dp->d_name)) + if (!filename_equal(pattern, dp->d_name, type)) continue; if (uflag && *count == 0) @@ -476,7 +480,7 @@ static void lookup(const char *pattern, struct wh_dirlist *ls, int want) for (; ls; ls = ls->next) { if ((ls->type & want) && ls->path) - findin(ls->path, patbuf, &count, &wait); + findin(ls->path, patbuf, &count, &wait, ls->type); } free(wait); From 062e6a39451622cce9fa89f26ee848b0dddb10d3 Mon Sep 17 00:00:00 2001 From: Samanta Navarro Date: Wed, 4 Nov 2020 11:39:00 +0000 Subject: [PATCH 8/8] whereis: extend test case Previous commits are covered with these test cases. Removed dependency on system layout. Signed-off-by: Samanta Navarro --- tests/expected/misc/whereis | 6 +++++- tests/ts/misc/whereis | 32 ++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/tests/expected/misc/whereis b/tests/expected/misc/whereis index 2e9ba477f..69d1a6ef2 100644 --- a/tests/expected/misc/whereis +++ b/tests/expected/misc/whereis @@ -1 +1,5 @@ -success +fsck success +fsck.ext4 success +python success +python3 success +python3.8 success diff --git a/tests/ts/misc/whereis b/tests/ts/misc/whereis index ee7ec2701..44643aefa 100755 --- a/tests/ts/misc/whereis +++ b/tests/ts/misc/whereis @@ -20,11 +20,31 @@ ts_init "$*" ts_check_test_command "$TS_CMD_WHEREIS" -LS_COUNT=$($TS_CMD_WHEREIS ls | wc -w) -if [ $LS_COUNT -lt 2 ]; then - echo "ls binary nor manual not found?" > $TS_OUTPUT -else - echo "success" > $TS_OUTPUT -fi +BIN_DIR="$(mktemp -d "${TS_OUTDIR}/binXXXXXXXXXXXXX")" +MAN_DIR="$(mktemp -d "${TS_OUTDIR}/manXXXXXXXXXXXXX")" +touch "$BIN_DIR/fsck" +touch "$MAN_DIR/fsck.8.zst" +touch "$BIN_DIR/fsck.ext4" +touch "$MAN_DIR/fsck.ext4.8.zst" +touch "$BIN_DIR/fsck.minix" +touch "$BIN_DIR/python" +touch "$MAN_DIR/python.1.gz" +touch "$BIN_DIR/python3" +touch "$MAN_DIR/python3.1" +touch "$BIN_DIR/python3.8" +touch "$BIN_DIR/python3.8-config" +touch "$MAN_DIR/python3.8.1" + +for COMMAND in fsck fsck.ext4 python python3 python3.8 +do + COUNT=$($TS_CMD_WHEREIS -B $BIN_DIR -M $MAN_DIR -f $COMMAND | wc -w) + if [ $COUNT -eq 3 ]; then + echo "$COMMAND success" >> $TS_OUTPUT + else + echo "$COMMAND failure" >> $TS_OUTPUT + fi +done + +rm -rf "$BIN_DIR" "$MAN_DIR" ts_finalize