[BUILTIN] Fix "test -x" as root on FreeBSD 8

POSIX.1-2008 §4.4 "File Access Permission" sayeth:

	If execute permission is requested, access shall be granted
	if execute permission is granted to at least one user by the
	file permission bits or by an alternate access control
	mechanism; otherwise, access shall be denied.

For historical reasons, POSIX unfortunately also allows access() and
faccessat() to return success for X_OK if the current process is
privileged, even when the above condition is not fulfilled and actual
execution would fail.  On the affected platforms, "test -x <path>" as
root started returning true on nonexecutable files when dash switched
from its own emulation to the true faccessat in v0.5.7~54
(2010-04-02).

Work around this by checking the permissions bits when mode == X_OK
and geteuid() == 0 on such platforms.

Unfortunately the behavior seems to vary from one kernel version to
another, so we cannot just check the behavior at compile time and rely
on that.  A survey of some affected kernels:

 - NetBSD's kernel moved to the sane semantics in 1997
 - OpenBSD's kernel made the same change in version 4.4, three years
   ago
 - FreeBSD 9's kernel fixes this but hasn't been released yet

It seems safe to only apply the workaround on systems using the
FreeBSD kernel for now, and to push for standardization on the
expected access()/faccessat() semantics so we can drop the workaround
altogether in a few years.

To try it on other platforms, use "./configure --enable-test-workaround".

Reported-by: Christoph Egger <christoph@debian.org>
Analysis-by: Petr Salinger <Petr.Salinger@seznam.cz>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Jonathan Nieder 2011-09-27 18:19:06 -05:00 committed by Herbert Xu
parent d0ed169b45
commit f6d4def4e2
2 changed files with 51 additions and 0 deletions

View File

@ -90,6 +90,37 @@ AC_CHECK_FUNCS(bsearch faccessat getpwnam getrlimit isalpha killpg \
sigsetmask stpcpy strchrnul strsignal strtod strtoimax \
strtoumax sysconf)
dnl Check whether it's worth working around FreeBSD PR kern/125009.
dnl The traditional behavior of access/faccessat is crazy, but
dnl POSIX.1-2008 explicitly allows those functions to misbehave.
dnl
dnl Unaffected kernels:
dnl
dnl - all versions of Linux
dnl - NetBSD sys/kern/vfs_subr.c 1.64, 1997-04-23
dnl - FreeBSD 9 (r212002), 2010-09-10
dnl - OpenBSD sys/kern/vfs_subr.c 1.166, 2008-06-09
dnl
dnl Also worked around in Debian's libc0.1 2.13-19 when using
dnl kFreeBSD 8.
AC_ARG_ENABLE(test-workaround, AS_HELP_STRING(--enable-test-workaround, \
[Guard against faccessat(2) that tells root all files are executable]),,
[enable_test_workaround=auto])
if test "enable_test_workaround" = "auto" &&
test "$ac_cv_func_faccessat" = yes; then
case `uname -s 2>/dev/null` in
GNU/kFreeBSD | \
FreeBSD)
enable_test_workaround=yes
esac
fi
if test "$enable_test_workaround" = "yes"; then
AC_DEFINE([HAVE_TRADITIONAL_FACCESSAT], [1],
[Define if your faccessat tells root all files are executable])
fi
if test "$enable_fnmatch" = yes; then
use_fnmatch=
AC_CHECK_FUNCS(fnmatch, use_fnmatch=yes)

View File

@ -155,6 +155,14 @@ static int test_st_mode(const struct stat64 *, int);
static int bash_group_member(gid_t);
#endif
#ifdef HAVE_FACCESSAT
# ifdef HAVE_TRADITIONAL_FACCESSAT
static inline int faccessat_confused_about_superuser(void) { return 1; }
# else
static inline int faccessat_confused_about_superuser(void) { return 0; }
# endif
#endif
static inline intmax_t getn(const char *s)
{
return atomax10(s);
@ -493,8 +501,20 @@ equalf (const char *f1, const char *f2)
}
#ifdef HAVE_FACCESSAT
static int has_exec_bit_set(const char *path)
{
struct stat64 st;
if (stat64(path, &st))
return 0;
return st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH);
}
static int test_file_access(const char *path, int mode)
{
if (faccessat_confused_about_superuser() &&
mode == X_OK && geteuid() == 0 && !has_exec_bit_set(path))
return 0;
return !faccessat(AT_FDCWD, path, mode, AT_EACCESS);
}
#else /* HAVE_FACCESSAT */