hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame sysdeps/unix/sysv/linux/test-errno-linux.c

Packit 6c4009
/* Test that failing system calls do set errno to the correct value.
Packit 6c4009
   Linux sycalls version.
Packit 6c4009
Packit 6c4009
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <array_length.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <mqueue.h>
Packit 6c4009
#include <sched.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/epoll.h>
Packit 6c4009
#include <sys/eventfd.h>
Packit 6c4009
#include <sys/file.h>
Packit 6c4009
#include <sys/fsuid.h>
Packit 6c4009
#include <sys/inotify.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/poll.h>
Packit 6c4009
#include <sys/quota.h>
Packit 6c4009
#include <sys/resource.h>
Packit 6c4009
#include <sys/select.h>
Packit 6c4009
#include <sys/sendfile.h>
Packit 6c4009
#include <sys/swap.h>
Packit 6c4009
#include <sys/time.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <sys/wait.h>
Packit 6c4009
Packit 6c4009
/* This is not an exhaustive test: only system calls that can be
Packit 6c4009
   persuaded to fail with a consistent error code and no side effects
Packit 6c4009
   are included.  Usually these are failures due to invalid arguments,
Packit 6c4009
   with errno code EBADF or EINVAL.  The order of argument checks is
Packit 6c4009
   unspecified, so we must take care to provide arguments that only
Packit 6c4009
   allow _one_ failure mode.
Packit 6c4009
Packit 6c4009
   Note that all system calls that can fail with EFAULT are permitted
Packit 6c4009
   to deliver a SIGSEGV signal instead, so we avoid supplying invalid
Packit 6c4009
   pointers in general, and we do not attempt to test system calls
Packit 6c4009
   that can only fail with EFAULT (e.g. gettimeofday, gethostname).
Packit 6c4009
Packit 6c4009
   Also note that root-only system calls (e.g. acct, reboot) may, when
Packit 6c4009
   the test is run as an unprivileged user, fail due to insufficient
Packit 6c4009
   privileges before bothering to do argument checks, so those are not
Packit 6c4009
   tested either.
Packit 6c4009
Packit 6c4009
   Also, system calls that take enum or a set of flags as argument is
Packit 6c4009
   not tested if POSIX doesn't specify exact binary values for all
Packit 6c4009
   flags, and so any value passed to flags may become valid.
Packit 6c4009
Packit 6c4009
   Some tests assume "/bin/sh" names a file that exists and is not a
Packit 6c4009
   directory.  */
Packit 6c4009
Packit 6c4009
/* Evalutes to the arguments in a list initializer which can be used
Packit 6c4009
   as a single macro argument.  */
Packit 6c4009
#define LIST(...) { __VA_ARGS__ }
Packit 6c4009
Packit 6c4009
/* This macro is necessary to forward the output of LIST as a macro
Packit 6c4009
   argument.  */
Packit 6c4009
#define LIST_FORWARD(...) __VA_ARGS__
Packit 6c4009
Packit 6c4009
/* Return true if CODE is contained in the array [CODES, CODES +
Packit 6c4009
   COUNT].  */
Packit 6c4009
static bool
Packit 6c4009
check_error_in_list (int code, int *codes, size_t count)
Packit 6c4009
{
Packit 6c4009
  for (size_t i = 0; i < count; ++i)
Packit 6c4009
    if (codes[i] == code)
Packit 6c4009
      return true;
Packit 6c4009
  return false;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define test_wrp_rv(rtype, prtype, experr_list, syscall, ...)	\
Packit 6c4009
  (__extension__ ({						\
Packit 6c4009
    errno = 0xdead;						\
Packit 6c4009
    int experr[] = experr_list;					\
Packit 6c4009
    rtype ret = syscall (__VA_ARGS__);				\
Packit 6c4009
    int err = errno;						\
Packit 6c4009
    int fail;							\
Packit 6c4009
    if ((ret == (rtype) -1)					\
Packit 6c4009
	&& check_error_in_list (err, experr, array_length (experr))) \
Packit 6c4009
      fail = 0;							\
Packit 6c4009
    else							\
Packit 6c4009
      {								\
Packit 6c4009
        fail = 1;						\
Packit 6c4009
        if (ret != (rtype) -1)					\
Packit 6c4009
          printf ("FAIL: " #syscall ": didn't fail as expected"	\
Packit 6c4009
		  " (return "prtype")\n", ret);			\
Packit 6c4009
        else if (err == 0xdead)					\
Packit 6c4009
          puts ("FAIL: " #syscall ": didn't update errno");	\
Packit 6c4009
	else							\
Packit 6c4009
          printf ("FAIL: " #syscall				\
Packit 6c4009
		  ": errno is: %d (%s) expected one of %s\n",	\
Packit 6c4009
		  err, strerror (err), #experr_list);		\
Packit 6c4009
      }								\
Packit 6c4009
    fail;							\
Packit 6c4009
  }))
Packit 6c4009
Packit 6c4009
#define test_wrp(experr, syscall, ...)				\
Packit 6c4009
  test_wrp_rv(int, "%d", LIST (experr), syscall, __VA_ARGS__)
Packit 6c4009
Packit 6c4009
#define test_wrp2(experr, syscall, ...)		\
Packit 6c4009
  test_wrp_rv(int, "%d", LIST_FORWARD (experr), syscall, __VA_ARGS__)
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  fd_set rs, ws, es;
Packit 6c4009
  int status;
Packit 6c4009
  off_t off;
Packit 6c4009
  stack_t ss;
Packit 6c4009
  struct dqblk dqblk;
Packit 6c4009
  struct epoll_event epoll_event;
Packit 6c4009
  struct pollfd pollfd;
Packit 6c4009
  struct sched_param sch_param;
Packit 6c4009
  struct timespec ts;
Packit 6c4009
  struct timeval tv;
Packit 6c4009
  unsigned char vec[16];
Packit 6c4009
  ss.ss_flags = ~SS_DISABLE;
Packit 6c4009
  ts.tv_sec = -1;
Packit 6c4009
Packit 6c4009
  int fails = 0;
Packit 6c4009
  fails |= test_wrp (EINVAL, epoll_create, -1);
Packit 6c4009
  fails |= test_wrp (EINVAL, epoll_create1, EPOLL_CLOEXEC + 1);
Packit 6c4009
  fails |= test_wrp (EBADF, epoll_ctl, -1, EPOLL_CTL_ADD, 0, &epoll_event);
Packit 6c4009
  fails |= test_wrp (EBADF, epoll_wait, -1, &epoll_event, 1, 1);
Packit 6c4009
  fails |= test_wrp (EBADF, fdatasync, -1);
Packit 6c4009
  fails |= test_wrp (EBADF, flock, -1, LOCK_SH);
Packit 6c4009
  fails |= test_wrp (ESRCH, getpgid, -1);
Packit 6c4009
  /* Linux v3.8 (676a0675c) removed the test to check at least one valid
Packit 6c4009
     bit in flags (to return EINVAL).  It was later added back in v3.9
Packit 6c4009
     (04df32fa1).  */
Packit 6c4009
  fails |= test_wrp2 (LIST (EINVAL, EBADF), inotify_add_watch, -1, "/", 0);
Packit 6c4009
  fails |= test_wrp (EINVAL, mincore, (void *) -1, 0, vec);
Packit 6c4009
  /* mlock fails if the result of the addition addr+len was less than addr
Packit 6c4009
     (which indicates final address overflow), however on 32 bits binaries
Packit 6c4009
     running on 64 bits kernels, internal syscall address check won't result
Packit 6c4009
     in an invalid address and thus syscalls fails later in vma
Packit 6c4009
     allocation.  */
Packit 6c4009
  fails |= test_wrp2 (LIST (EINVAL, ENOMEM), mlock, (void *) -1, 1);
Packit 6c4009
  fails |= test_wrp (EINVAL, nanosleep, &ts, &ts);
Packit 6c4009
  fails |= test_wrp (EINVAL, poll, &pollfd, -1, 0);
Packit 6c4009
  /* quotactl returns ENOSYS for kernels not configured with
Packit 6c4009
     CONFIG_QUOTA, and may return EPERM if called within certain types
Packit 6c4009
     of containers.  */
Packit 6c4009
  fails |= test_wrp2 (LIST (ENODEV, ENOSYS, EPERM),
Packit 6c4009
		      quotactl, Q_GETINFO, NULL, -1, (caddr_t) &dqblk);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_getparam, -1, &sch_param);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_getscheduler, -1);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_get_priority_max, -1);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_get_priority_min, -1);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_rr_get_interval, -1, &ts);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_setparam, -1, &sch_param);
Packit 6c4009
  fails |= test_wrp (EINVAL, sched_setscheduler, -1, 0, &sch_param);
Packit 6c4009
  fails |= test_wrp (EINVAL, select, -1, &rs, &ws, &es, &tv;;
Packit 6c4009
  fails |= test_wrp (EBADF, sendfile, -1, -1, &off, 0);
Packit 6c4009
  fails |= test_wrp (EINVAL, sigaltstack, &ss, NULL);
Packit 6c4009
  fails |= test_wrp (ECHILD, wait4, -1, &status, 0, NULL);
Packit 6c4009
Packit 6c4009
  return fails;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#include "support/test-driver.c"