Re: [PATCHv2 2/3] selftests: Add test of O_BENEATH & openat(2)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 4, 2014 at 1:54 AM, David Drysdale <drysdale@xxxxxxxxxx> wrote:
> Add simple tests of openat(2) variations, including examples that
> check the new O_BENEATH flag.
>
> Signed-off-by: David Drysdale <drysdale@xxxxxxxxxx>

Excellent! Thank you for including self tests. :)

Acked-by: Kees Cook <keescook@xxxxxxxxxxxx>

-Kees

> ---
>  tools/testing/selftests/Makefile          |   1 +
>  tools/testing/selftests/openat/.gitignore |   4 +
>  tools/testing/selftests/openat/Makefile   |  28 +++++
>  tools/testing/selftests/openat/openat.c   | 180 ++++++++++++++++++++++++++++++
>  4 files changed, 213 insertions(+)
>  create mode 100644 tools/testing/selftests/openat/.gitignore
>  create mode 100644 tools/testing/selftests/openat/Makefile
>  create mode 100644 tools/testing/selftests/openat/openat.c
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 36ff2e4c7b6f..812e973233d2 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -14,6 +14,7 @@ TARGETS += powerpc
>  TARGETS += user
>  TARGETS += sysctl
>  TARGETS += firmware
> +TARGETS += openat
>
>  TARGETS_HOTPLUG = cpu-hotplug
>  TARGETS_HOTPLUG += memory-hotplug
> diff --git a/tools/testing/selftests/openat/.gitignore b/tools/testing/selftests/openat/.gitignore
> new file mode 100644
> index 000000000000..835b2dcd8678
> --- /dev/null
> +++ b/tools/testing/selftests/openat/.gitignore
> @@ -0,0 +1,4 @@
> +openat
> +subdir
> +topfile
> +symlinkdown
> \ No newline at end of file
> diff --git a/tools/testing/selftests/openat/Makefile b/tools/testing/selftests/openat/Makefile
> new file mode 100644
> index 000000000000..84cd06e7ee82
> --- /dev/null
> +++ b/tools/testing/selftests/openat/Makefile
> @@ -0,0 +1,28 @@
> +CC = $(CROSS_COMPILE)gcc
> +CFLAGS = -Wall
> +BINARIES = openat
> +DEPS = subdir topfile symlinkdown subdir/bottomfile subdir/symlinkup subdir/symlinkout subdir/symlinkin
> +all: $(BINARIES) $(DEPS)
> +
> +subdir:
> +       mkdir -p subdir
> +topfile:
> +       echo 0123456789 > $@
> +subdir/bottomfile: | subdir
> +       echo 0123456789 > $@
> +subdir/symlinkup: | subdir
> +       ln -s ../topfile $@
> +subdir/symlinkout: | subdir
> +       ln -s /etc/passwd $@
> +subdir/symlinkin: | subdir
> +       ln -s bottomfile $@
> +symlinkdown:
> +       ln -s subdir/bottomfile $@
> +%: %.c
> +       $(CC) $(CFLAGS) -o $@ $^
> +
> +run_tests: all
> +       ./openat
> +
> +clean:
> +       rm -rf $(BINARIES) $(DEPS)
> diff --git a/tools/testing/selftests/openat/openat.c b/tools/testing/selftests/openat/openat.c
> new file mode 100644
> index 000000000000..0c030cbd10dc
> --- /dev/null
> +++ b/tools/testing/selftests/openat/openat.c
> @@ -0,0 +1,180 @@
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/syscall.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +#include <linux/fcntl.h>
> +
> +/* Bypass glibc */
> +static int openat_(int dirfd, const char *pathname, int flags)
> +{
> +       return syscall(__NR_openat, dirfd, pathname, flags);
> +}
> +
> +static int openat_or_die(int dfd, const char *path, int flags)
> +{
> +       int fd = openat_(dfd, path, flags);
> +
> +       if (fd < 0) {
> +               printf("Failed to openat(%d, '%s'); "
> +                       "check prerequisites are available\n", dfd, path);
> +               exit(1);
> +       }
> +       return fd;
> +}
> +
> +static int check_openat(int dfd, const char *path, int flags)
> +{
> +       int rc;
> +       int fd;
> +       char buffer[4];
> +
> +       errno = 0;
> +       printf("Check success of openat(%d, '%s', %x)... ",
> +              dfd, path?:"(null)", flags);
> +       fd = openat_(dfd, path, flags);
> +       if (fd < 0) {
> +               printf("[FAIL]: openat() failed, rc=%d errno=%d (%s)\n",
> +                       fd, errno, strerror(errno));
> +               return 1;
> +       }
> +       errno = 0;
> +       rc = read(fd, buffer, sizeof(buffer));
> +       if (rc < 0) {
> +               printf("[FAIL]: read() failed, rc=%d errno=%d (%s)\n",
> +                       rc, errno, strerror(errno));
> +               return 1;
> +       }
> +       close(fd);
> +       printf("[OK]\n");
> +       return 0;
> +}
> +
> +#define check_openat_fail(dfd, path, flags, errno)     \
> +       _check_openat_fail(dfd, path, flags, errno, #errno)
> +static int _check_openat_fail(int dfd, const char *path, int flags,
> +                             int expected_errno, const char *errno_str)
> +{
> +       int rc;
> +
> +       errno = 0;
> +       printf("Check failure of openat(%d, '%s', %x) with %s... ",
> +               dfd, path?:"(null)", flags, errno_str);
> +       rc = openat_(dfd, path, flags);
> +       if (rc > 0) {
> +               printf("[FAIL] (unexpected success from openat(2))\n");
> +               close(rc);
> +               return 1;
> +       }
> +       if (errno != expected_errno) {
> +               printf("[FAIL] (expected errno %d (%s) not %d (%s)\n",
> +                       expected_errno, strerror(expected_errno),
> +                       errno, strerror(errno));
> +               return 1;
> +       }
> +       printf("[OK]\n");
> +       return 0;
> +}
> +
> +int check_proc(void)
> +{
> +       int proc_dfd = openat_(AT_FDCWD, "/proc/self", O_RDONLY);
> +       int fail = 0;
> +
> +       if (proc_dfd < 0) {
> +               printf("'/proc/self' unavailable (errno=%d '%s'), skipping\n",
> +                       errno, strerror(errno));
> +               return 1;
> +       }
> +       fail |= check_openat(proc_dfd, "root/etc/passwd", O_RDONLY);
> +#ifdef O_BENEATH
> +       fail |= check_openat_fail(proc_dfd, "root/etc/passwd",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +#endif
> +       return fail;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       int fail = 0;
> +       int dot_dfd = openat_or_die(AT_FDCWD, ".", O_RDONLY);
> +       int subdir_dfd = openat_or_die(AT_FDCWD, "subdir", O_RDONLY);
> +       int file_fd = openat_or_die(AT_FDCWD, "topfile", O_RDONLY);
> +
> +       /* Sanity check normal behavior */
> +       fail |= check_openat(AT_FDCWD, "topfile", O_RDONLY);
> +       fail |= check_openat(AT_FDCWD, "subdir/bottomfile", O_RDONLY);
> +
> +       fail |= check_openat(dot_dfd, "topfile", O_RDONLY);
> +       fail |= check_openat(dot_dfd, "subdir/bottomfile", O_RDONLY);
> +       fail |= check_openat(dot_dfd, "subdir/../topfile", O_RDONLY);
> +
> +       fail |= check_openat(subdir_dfd, "../topfile", O_RDONLY);
> +       fail |= check_openat(subdir_dfd, "bottomfile", O_RDONLY);
> +       fail |= check_openat(subdir_dfd, "../subdir/bottomfile", O_RDONLY);
> +       fail |= check_openat(subdir_dfd, "symlinkup", O_RDONLY);
> +       fail |= check_openat(subdir_dfd, "symlinkout", O_RDONLY);
> +
> +       fail |= check_openat(AT_FDCWD, "/etc/passwd", O_RDONLY);
> +       fail |= check_openat(dot_dfd, "/etc/passwd", O_RDONLY);
> +       fail |= check_openat(subdir_dfd, "/etc/passwd", O_RDONLY);
> +
> +       fail |= check_openat_fail(AT_FDCWD, "bogus", O_RDONLY, ENOENT);
> +       fail |= check_openat_fail(dot_dfd, "bogus", O_RDONLY, ENOENT);
> +       fail |= check_openat_fail(999, "bogus", O_RDONLY, EBADF);
> +       fail |= check_openat_fail(file_fd, "bogus", O_RDONLY, ENOTDIR);
> +
> +#ifdef O_BENEATH
> +       /* Test out O_BENEATH */
> +       fail |= check_openat(AT_FDCWD, "topfile", O_RDONLY|O_BENEATH);
> +       fail |= check_openat(AT_FDCWD, "subdir/bottomfile",
> +                            O_RDONLY|O_BENEATH);
> +
> +       fail |= check_openat(dot_dfd, "topfile", O_RDONLY|O_BENEATH);
> +       fail |= check_openat(dot_dfd, "subdir/bottomfile",
> +                            O_RDONLY|O_BENEATH);
> +       fail |= check_openat(subdir_dfd, "bottomfile", O_RDONLY|O_BENEATH);
> +
> +       /* Symlinks without .. or leading / are OK */
> +       fail |= check_openat(dot_dfd, "symlinkdown", O_RDONLY|O_BENEATH);
> +       fail |= check_openat(dot_dfd, "subdir/symlinkin", O_RDONLY|O_BENEATH);
> +       fail |= check_openat(subdir_dfd, "symlinkin", O_RDONLY|O_BENEATH);
> +       /* ... unless of course we specify O_NOFOLLOW */
> +       fail |= check_openat_fail(dot_dfd, "symlinkdown",
> +                                 O_RDONLY|O_BENEATH|O_NOFOLLOW, ELOOP);
> +       fail |= check_openat_fail(dot_dfd, "subdir/symlinkin",
> +                                 O_RDONLY|O_BENEATH|O_NOFOLLOW, ELOOP);
> +       fail |= check_openat_fail(subdir_dfd, "symlinkin",
> +                                 O_RDONLY|O_BENEATH|O_NOFOLLOW, ELOOP);
> +
> +       /* Can't open paths with ".." in them */
> +       fail |= check_openat_fail(dot_dfd, "subdir/../topfile",
> +                               O_RDONLY|O_BENEATH, EPERM);
> +       fail |= check_openat_fail(subdir_dfd, "../topfile",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +       fail |= check_openat_fail(subdir_dfd, "../subdir/bottomfile",
> +                               O_RDONLY|O_BENEATH, EPERM);
> +
> +       /* Can't open paths starting with "/" */
> +       fail |= check_openat_fail(AT_FDCWD, "/etc/passwd",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +       fail |= check_openat_fail(dot_dfd, "/etc/passwd",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +       fail |= check_openat_fail(subdir_dfd, "/etc/passwd",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +       /* Can't sneak around constraints with symlinks */
> +       fail |= check_openat_fail(subdir_dfd, "symlinkup",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +       fail |= check_openat_fail(subdir_dfd, "symlinkout",
> +                                 O_RDONLY|O_BENEATH, EPERM);
> +#else
> +       printf("Skipping O_BENEATH tests due to missing #define\n");
> +#endif
> +       fail |= check_proc();
> +
> +       return fail ? -1 : 0;
> +}
> --
> 2.1.0.rc2.206.gedb03e5
>



-- 
Kees Cook
Chrome OS Security
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux