1 /* 2 * Strictly speaking, this is not a test. But it can report during test 3 * runs so relative performace can be measured. 4 */ 5 #define _GNU_SOURCE 6 #include <assert.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <unistd.h> 11 #include <linux/filter.h> 12 #include <linux/seccomp.h> 13 #include <sys/prctl.h> 14 #include <sys/syscall.h> 15 #include <sys/types.h> 16 17 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 18 19 unsigned long long timing(clockid_t clk_id, unsigned long long samples) 20 { 21 struct timespec start, finish; 22 unsigned long long i; 23 pid_t pid, ret; 24 25 pid = getpid(); 26 assert(clock_gettime(clk_id, &start) == 0); 27 for (i = 0; i < samples; i++) { 28 ret = syscall(__NR_getpid); 29 assert(pid == ret); 30 } 31 assert(clock_gettime(clk_id, &finish) == 0); 32 33 i = finish.tv_sec - start.tv_sec; 34 i *= 1000000000ULL; 35 i += finish.tv_nsec - start.tv_nsec; 36 37 printf("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n", 38 finish.tv_sec, finish.tv_nsec, 39 start.tv_sec, start.tv_nsec, 40 i, (double)i / 1000000000.0); 41 42 return i; 43 } 44 45 unsigned long long calibrate(void) 46 { 47 struct timespec start, finish; 48 unsigned long long i, samples, step = 9973; 49 pid_t pid, ret; 50 int seconds = 15; 51 52 printf("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds); 53 54 samples = 0; 55 pid = getpid(); 56 assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0); 57 do { 58 for (i = 0; i < step; i++) { 59 ret = syscall(__NR_getpid); 60 assert(pid == ret); 61 } 62 assert(clock_gettime(CLOCK_MONOTONIC, &finish) == 0); 63 64 samples += step; 65 i = finish.tv_sec - start.tv_sec; 66 i *= 1000000000ULL; 67 i += finish.tv_nsec - start.tv_nsec; 68 } while (i < 1000000000ULL); 69 70 return samples * seconds; 71 } 72 73 int main(int argc, char *argv[]) 74 { 75 struct sock_filter filter[] = { 76 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 77 }; 78 struct sock_fprog prog = { 79 .len = (unsigned short)ARRAY_SIZE(filter), 80 .filter = filter, 81 }; 82 long ret; 83 unsigned long long samples; 84 unsigned long long native, filter1, filter2; 85 86 printf("Current BPF sysctl settings:\n"); 87 system("sysctl net.core.bpf_jit_enable"); 88 system("sysctl net.core.bpf_jit_harden"); 89 90 if (argc > 1) 91 samples = strtoull(argv[1], NULL, 0); 92 else 93 samples = calibrate(); 94 95 printf("Benchmarking %llu syscalls...\n", samples); 96 97 /* Native call */ 98 native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 99 printf("getpid native: %llu ns\n", native); 100 101 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 102 assert(ret == 0); 103 104 /* One filter */ 105 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); 106 assert(ret == 0); 107 108 filter1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 109 printf("getpid RET_ALLOW 1 filter: %llu ns\n", filter1); 110 111 if (filter1 == native) 112 printf("No overhead measured!? Try running again with more samples.\n"); 113 114 /* Two filters */ 115 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); 116 assert(ret == 0); 117 118 filter2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 119 printf("getpid RET_ALLOW 2 filters: %llu ns\n", filter2); 120 121 /* Calculations */ 122 printf("Estimated total seccomp overhead for 1 filter: %llu ns\n", 123 filter1 - native); 124 125 printf("Estimated total seccomp overhead for 2 filters: %llu ns\n", 126 filter2 - native); 127 128 printf("Estimated seccomp per-filter overhead: %llu ns\n", 129 filter2 - filter1); 130 131 printf("Estimated seccomp entry overhead: %llu ns\n", 132 filter1 - native - (filter2 - filter1)); 133 134 return 0; 135 } 136