gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/images/benchmarks/syscallbench/syscallbench.c (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <getopt.h>
    16  #include <linux/audit.h>
    17  #include <linux/filter.h>
    18  #include <linux/seccomp.h>
    19  #include <stdio.h>
    20  #include <stdlib.h>
    21  #include <sys/prctl.h>
    22  #include <sys/syscall.h>
    23  #include <unistd.h>
    24  
    25  static int loops = 10000000;
    26  
    27  enum syscall_type { get_pid, get_pid_opt };
    28  enum seccomp_policy { seccomp_none, seccomp_cacheable, seccomp_uncacheable };
    29  
    30  #ifdef __x86_64__
    31  
    32  #define SYSNO_STR1(x) #x
    33  #define SYSNO_STR(x) SYSNO_STR1(x)
    34  
    35  void do_getpidopt() {
    36    __asm__("movl $" SYSNO_STR(SYS_getpid) ", %%eax\n"
    37      "syscall\n"
    38      : : : "rax", "rcx", "r11");
    39  }
    40  #endif
    41  
    42  static void show_usage(const char *cmd) {
    43    fprintf(stderr,
    44            "Usage: %s [options]\n"
    45            "-l, --loops <num>\t\t Number of syscall loops, default 10000000\n"
    46            "-s, --syscall <num>\t\tSyscall to run (default getpid)\n"
    47            "--seccomp_cacheable\t\tAdd a cacheable ALLOW "
    48            "seccomp filter for this syscall\n"
    49            "--seccomp_notcacheable\t\tAdd a non-cacheable ALLOW "
    50            "seccomp filter for this syscall\n"
    51            "\tOptions:\n"
    52            "\t%d) getpid\n"
    53            "\t%d) getpidopt\n",
    54            cmd, get_pid, get_pid_opt);
    55  }
    56  
    57  static void set_cacheable_filter() {
    58    // "Prior to [PR_SET_SECCOMP], the task must call prctl(PR_SET_NO_NEW_PRIVS,
    59    // 1) or run with CAP_SYS_ADMIN privileges in its namespace." -
    60    // Documentation/prctl/seccomp_filter.txt
    61    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
    62      fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS) failed\n");
    63      exit(1);
    64    }
    65  
    66    struct sock_filter filter[] = {
    67        // A = seccomp_data.arch
    68        BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4),
    69        // if (A != AUDIT_ARCH_X86_64) goto kill
    70        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 2),
    71        // A = seccomp_data.nr
    72        BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0),
    73        // return SECCOMP_RET_ALLOW
    74        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    75        // kill: return SECCOMP_RET_KILL
    76        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
    77    };
    78    struct sock_fprog prog;
    79    prog.len = 5;
    80    prog.filter = filter;
    81    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) != 0) {
    82      fprintf(stderr, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed\n");
    83      exit(1);
    84    }
    85  }
    86  
    87  static void set_uncacheable_filter() {
    88    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
    89      fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS) failed\n");
    90      exit(1);
    91    }
    92  
    93    struct sock_filter filter[] = {
    94        // A = seccomp_data.arch
    95        BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4),
    96        // if (A != AUDIT_ARCH_X86_64) goto kill
    97        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 3),
    98        // A = seccomp_data.nr
    99        BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0),
   100        // A = seccomp_data.args[0]
   101        BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 16),
   102        // return SECCOMP_RET_ALLOW
   103        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
   104        // kill: return SECCOMP_RET_KILL
   105        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
   106    };
   107    struct sock_fprog prog;
   108    prog.len = 6;
   109    prog.filter = filter;
   110    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) != 0) {
   111      fprintf(stderr, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed\n");
   112      exit(1);
   113    }
   114  }
   115  
   116  int main(int argc, char *argv[]) {
   117    int i, c, sys_val = get_pid;
   118    int seccomp_policy_flag = seccomp_none;
   119    struct option long_options[] = {{"loops", required_argument, 0, 'l'},
   120                                    {"syscall", required_argument, 0, 's'},
   121                                    {"seccomp_cacheable", no_argument,
   122                                     &seccomp_policy_flag, seccomp_cacheable},
   123                                    {"seccomp_notcacheable", no_argument,
   124                                     &seccomp_policy_flag, seccomp_uncacheable},
   125                                    {0, 0, 0, 0}};
   126    int option_index = 0;
   127  
   128    while ((c = getopt_long(argc, argv, "l:s:c:", long_options, &option_index)) !=
   129           -1) {
   130      switch (c) {
   131        case 0:
   132          break;
   133        case 'l':
   134          loops = atoi(optarg);
   135          if (loops <= 0) {
   136            show_usage(argv[0]);
   137            exit(1);
   138          }
   139          break;
   140        case 's':
   141          sys_val = atoi(optarg);
   142          if (sys_val < 0) {
   143            show_usage(argv[0]);
   144            exit(1);
   145          }
   146          break;
   147        case 'c':
   148          sys_val = atoi(optarg);
   149          if (sys_val < 0) {
   150            show_usage(argv[0]);
   151            exit(1);
   152          }
   153          break;
   154        default:
   155          fprintf(stderr, "unknown option: '%c'\n", c);
   156          show_usage(argv[0]);
   157          exit(1);
   158      }
   159    }
   160  
   161    switch (seccomp_policy_flag) {
   162      case seccomp_none:
   163        break;
   164      case seccomp_cacheable:
   165        set_cacheable_filter();
   166        break;
   167      case seccomp_uncacheable:
   168        set_uncacheable_filter();
   169        break;
   170      default:
   171        fprintf(stderr, "unknown seccomp option: %d\n", seccomp_policy_flag);
   172        show_usage(argv[0]);
   173        exit(1);
   174    }
   175  
   176    switch (sys_val) {
   177      case (int)get_pid:
   178        for (i = 0; i < loops; i++) syscall(SYS_getpid);
   179        break;
   180      case (int)get_pid_opt:
   181        for (i = 0; i < loops; i++) do_getpidopt();
   182        break;
   183      default:
   184        fprintf(stderr, "unknown syscall option: %d\n", sys_val);
   185        show_usage(argv[0]);
   186        exit(1);
   187    }
   188  
   189    printf("# Executed %'d calls\n", loops);
   190    return 0;
   191  }