"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-seccomp.c" (15 Mar 2019, 8335 Bytes) of package /linux/privat/stress-ng-0.09.56.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "stress-seccomp.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.09.50_vs_0.09.51.

    1 /*
    2  * Copyright (C) 2013-2019 Canonical, Ltd.
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU General Public License
    6  * as published by the Free Software Foundation; either version 2
    7  * of the License, or (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   17  *
   18  * This code is a complete clean re-write of the stress tool by
   19  * Colin Ian King <colin.king@canonical.com> and attempts to be
   20  * backwardly compatible with the stress tool by Amos Waterland
   21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
   22  * functionality.
   23  *
   24  */
   25 #include "stress-ng.h"
   26 
   27 #if defined(HAVE_LINUX_SECCOMP_H) &&    \
   28     defined(HAVE_LINUX_AUDIT_H) &&  \
   29     defined(HAVE_LINUX_FILTER_H) && \
   30     defined(HAVE_SYS_PRCTL_H) &&    \
   31     defined(PR_SET_SECCOMP) &&      \
   32     defined(SECCOMP_SET_MODE_FILTER)
   33 
   34 #if defined(NSIG)
   35 #define MAX_SIGNUM  NSIG
   36 #elif defined(_NSIG)
   37 #define MAX_SIGNUM  _NSIG
   38 #else
   39 #define MAX_SIGNUM  256
   40 #endif
   41 
   42 #define EXIT_TRAPPED    255
   43 
   44 #define SYSCALL_NR  (offsetof(struct seccomp_data, nr))
   45 
   46 #define ALLOW_SYSCALL(syscall) \
   47     BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_##syscall, 0, 1), \
   48     BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
   49 
   50 static struct sock_filter filter_allow_all[] = {
   51     BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
   52 };
   53 
   54 static struct sock_filter filter_allow_write[] = {
   55     BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SYSCALL_NR),
   56 #if defined(__NR_open)
   57     ALLOW_SYSCALL(open),
   58 #endif
   59 #if defined(__NR_openat)
   60     ALLOW_SYSCALL(openat),
   61 #endif
   62 #if defined(__NR_write)
   63     ALLOW_SYSCALL(write),
   64 #endif
   65 #if defined(__NR_close)
   66     ALLOW_SYSCALL(close),
   67 #endif
   68 #if defined(__NR_exit_group)
   69     ALLOW_SYSCALL(exit_group),
   70 #endif
   71 #if defined(__NR_set_robust_list)
   72     ALLOW_SYSCALL(set_robust_list),
   73 #endif
   74     BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP)
   75 };
   76 
   77 static struct sock_filter filter[] = {
   78     BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SYSCALL_NR),
   79 #if defined(__NR_open)
   80     ALLOW_SYSCALL(open),
   81 #endif
   82 #if defined(__NR_openat)
   83     ALLOW_SYSCALL(openat),
   84 #endif
   85 #if defined(__NR_close)
   86     ALLOW_SYSCALL(close),
   87 #endif
   88 #if defined(__NR_exit_group)
   89     ALLOW_SYSCALL(exit_group),
   90 #endif
   91 #if defined(__NR_set_robust_list)
   92     ALLOW_SYSCALL(set_robust_list),
   93 #endif
   94     BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP)
   95 };
   96 
   97 static struct sock_filter filter_random[64];
   98 
   99 static struct sock_fprog prog_allow_all = {
  100     .len = (unsigned short)SIZEOF_ARRAY(filter_allow_all),
  101     .filter = filter_allow_all
  102 };
  103 
  104 static struct sock_fprog prog_allow_write = {
  105     .len = (unsigned short)SIZEOF_ARRAY(filter_allow_write),
  106     .filter = filter_allow_write
  107 };
  108 
  109 static struct sock_fprog prog = {
  110     .len = (unsigned short)SIZEOF_ARRAY(filter),
  111     .filter = filter
  112 };
  113 
  114 static struct sock_fprog prog_random = {
  115     .len = (unsigned short)SIZEOF_ARRAY(filter_random),
  116     .filter = filter_random
  117 };
  118 
  119 static int stress_seccomp_supported(void)
  120 {
  121     pid_t pid;
  122     int status;
  123 
  124     pid = fork();
  125     if (pid < 0) {
  126         pr_inf("seccomp stressor will be skipped, the check for seccomp failed, fork failed: errno=%d (%s)\n",
  127             errno, strerror(errno));
  128         return -1;
  129     }
  130     if (pid == 0) {
  131         if (shim_seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog_allow_all) == 0) {
  132             _exit(0);
  133         }
  134         _exit(errno);
  135     }
  136     if (waitpid(pid, &status, 0) < 0) {
  137         pr_inf("seccomp stressor will be skipped, the check for seccomp failed, wait failed: errno=%d (%s)\n",
  138             errno, strerror(errno));
  139         return -1;
  140     }
  141     if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
  142         errno = WEXITSTATUS(status);
  143         if (errno == EACCES) {
  144             pr_inf("seccomp stressor will be skipped, SECCOMP_SET_MODE_FILTER requires CAP_SYS_ADMIN capability\n");
  145             return -1;
  146         } else {
  147             pr_inf("seccomp stressor will be skipped, SECCOMP_SET_MODE_FILTER is not supported, errno=%d (%s)\n",
  148                 errno, strerror(errno));
  149         }
  150         return -1;
  151     }
  152     return 0;
  153 }
  154 
  155 static void MLOCKED_TEXT stress_sigsys(int signum)
  156 {
  157     (void)signum;
  158 
  159     _exit(EXIT_TRAPPED);
  160 }
  161 
  162 /*
  163  *  stress_seccomp_set_filter()
  164  *  set up a seccomp filter, allow writes
  165  *  if allow_write is true.
  166  */
  167 static inline int stress_seccomp_set_filter(
  168     const args_t *args,
  169     const bool allow_write,
  170     bool do_random)
  171 {
  172 #if defined(__NR_seccomp)
  173     static bool use_seccomp = true;
  174 #endif
  175 
  176     /*
  177      *  Depending on allow_write we either use the
  178      *  filter that allows writes or the filter
  179      *  that does not allow writes
  180      */
  181     size_t i;
  182     struct sock_fprog *p = allow_write ?
  183         &prog_allow_write :
  184             do_random ? &prog_random : &prog;
  185 
  186     if (do_random) {
  187         for (i = 0; i < SIZEOF_ARRAY(filter_random); i++) {
  188             struct sock_filter bpf_stmt = BPF_STMT(mwc32(), SECCOMP_RET_KILL);
  189             filter_random[i] = bpf_stmt;
  190         }
  191     }
  192 
  193     if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
  194         pr_fail_err("prctl PR_SET_NEW_PRIVS");
  195         return -1;
  196     }
  197 #if defined(__NR_seccomp)
  198     /*
  199      *  Try using the newer seccomp syscall first
  200      */
  201     if (use_seccomp) {
  202 redo_seccomp:
  203         if (shim_seccomp(SECCOMP_SET_MODE_FILTER, 0, p) == 0)
  204             return 0;
  205 
  206         if (errno != ENOSYS) {
  207             if (do_random) {
  208                 do_random = false;
  209                 p = &prog;
  210                 goto redo_seccomp;
  211             }
  212             pr_fail_err("seccomp SECCOMP_SET_MODE_FILTER");
  213             return -1;
  214         }
  215         use_seccomp = false;
  216     }
  217 #endif
  218 
  219 redo_prctl:
  220     if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, p) < 0) {
  221         if (do_random) {
  222             do_random = false;
  223             p = &prog;
  224             goto redo_prctl;
  225         }
  226         pr_fail_err("prctl PR_SET_SECCOMP");
  227         return -1;
  228     }
  229     return 0;
  230 }
  231 
  232 
  233 /*
  234  *  stress_seccomp()
  235  *  stress seccomp
  236  */
  237 static int stress_seccomp(const args_t *args)
  238 {
  239     do {
  240         pid_t pid;
  241         const bool allow_write = (mwc32() % 2) != 0;
  242         const bool do_random = (mwc32() % 20) != 0;
  243 
  244         pid = fork();
  245         if (pid == -1) {
  246             pr_fail_err("fork");
  247             break;
  248         }
  249         if (pid == 0) {
  250             /*
  251              *  The child has a seccomp filter applied and
  252              *  1 in 50 chance that write() is not allowed
  253              *  causing seccomp to kill it and the parent
  254              *  sees it die on a SIGSYS
  255              */
  256             int fd, rc = EXIT_SUCCESS, ret;
  257 
  258             stress_process_dumpable(false);
  259             ret = stress_sighandler(args->name, SIGSYS, stress_sigsys, NULL);
  260             (void)ret;
  261 
  262             if (stress_seccomp_set_filter(args, allow_write, do_random) < 0)
  263                 _exit(EXIT_FAILURE);
  264             if ((fd = open("/dev/null", O_WRONLY)) < 0) {
  265                 pr_err("%s: open failed on /dev/null, "
  266                     "errno=%d (%s)\n",
  267                     args->name, errno, strerror(errno));
  268                 _exit(EXIT_FAILURE);
  269             }
  270             if (write(fd, "TEST\n", 5) < 0) {
  271                 pr_err("%s: write to /dev/null failed, "
  272                     "errno=%d (%s)\n",
  273                     args->name, errno, strerror(errno));
  274                 rc = EXIT_FAILURE;
  275             }
  276             (void)close(fd);
  277             _exit(rc);
  278         }
  279         if (pid > 0) {
  280             int status;
  281 
  282             /* Wait for child to exit or get killed by seccomp */
  283             if (waitpid(pid, &status, 0) < 0) {
  284                 if (errno != EINTR)
  285                     pr_dbg("%s: waitpid failed, errno = %d (%s)\n",
  286                         args->name, errno, strerror(errno));
  287             } else {
  288                 /* Did the child hit a weird error? */
  289                 if (WIFEXITED(status) &&
  290                     (WEXITSTATUS(status) != EXIT_TRAPPED) &&
  291                     (WEXITSTATUS(status) != EXIT_SUCCESS)) {
  292                     pr_fail("%s: aborting because of unexpected "
  293                         "failure in child process\n", args->name);
  294                     return EXIT_FAILURE;
  295                 }
  296                 /* ..exited OK but we expected trapped SIGSYS death? */
  297                 if (WIFEXITED(status) && !allow_write &&
  298                     (WEXITSTATUS(status) != EXIT_TRAPPED)) {
  299                     pr_fail("%s: expecting SIGSYS seccomp trap "
  300                         "but got a successful exit which "
  301                         "was not expected\n",
  302                         args->name);
  303                 }
  304                 /* ..exited with a SIGSYS but we expexted OK exit? */
  305                 if (WIFSIGNALED(status) && allow_write) {
  306                     if (WTERMSIG(status) == SIGSYS) {
  307                         pr_fail("%s: expecting a successful exit "
  308                             "but got a seccomp SIGSYS "
  309                             "which was not expected\n",
  310                             args->name);
  311                     }
  312                 }
  313             }
  314         }
  315         inc_counter(args);
  316     } while (keep_stressing());
  317 
  318     return EXIT_SUCCESS;
  319 }
  320 
  321 stressor_info_t stress_seccomp_info = {
  322     .stressor = stress_seccomp,
  323     .supported = stress_seccomp_supported,
  324     .class = CLASS_OS
  325 };
  326 #else
  327 stressor_info_t stress_seccomp_info = {
  328     .stressor = stress_not_implemented,
  329     .class = CLASS_OS
  330 };
  331 #endif