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 }