Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/seccomp: Compare bitmap vs filter overhead

As part of the seccomp benchmarking, include the expectations with
regard to the timing behavior of the constant action bitmaps, and report
inconsistencies better.

Example output with constant action bitmaps on x86:

$ sudo ./seccomp_benchmark 100000000
Current BPF sysctl settings:
net.core.bpf_jit_enable = 1
net.core.bpf_jit_harden = 0
Benchmarking 200000000 syscalls...
129.359381409 - 0.008724424 = 129350656985 (129.4s)
getpid native: 646 ns
264.385890006 - 129.360453229 = 135025436777 (135.0s)
getpid RET_ALLOW 1 filter (bitmap): 675 ns
399.400511893 - 264.387045901 = 135013465992 (135.0s)
getpid RET_ALLOW 2 filters (bitmap): 675 ns
545.872866260 - 399.401718327 = 146471147933 (146.5s)
getpid RET_ALLOW 3 filters (full): 732 ns
696.337101319 - 545.874097681 = 150463003638 (150.5s)
getpid RET_ALLOW 4 filters (full): 752 ns
Estimated total seccomp overhead for 1 bitmapped filter: 29 ns
Estimated total seccomp overhead for 2 bitmapped filters: 29 ns
Estimated total seccomp overhead for 3 full filters: 86 ns
Estimated total seccomp overhead for 4 full filters: 106 ns
Estimated seccomp entry overhead: 29 ns
Estimated seccomp per-filter overhead (last 2 diff): 20 ns
Estimated seccomp per-filter overhead (filters / 4): 19 ns
Expectations:
native ≤ 1 bitmap (646 ≤ 675): ✔️
native ≤ 1 filter (646 ≤ 732): ✔️
per-filter (last 2 diff) ≈ per-filter (filters / 4) (20 ≈ 19): ✔️
1 bitmapped ≈ 2 bitmapped (29 ≈ 29): ✔️
entry ≈ 1 bitmapped (29 ≈ 29): ✔️
entry ≈ 2 bitmapped (29 ≈ 29): ✔️
native + entry + (per filter * 4) ≈ 4 filters total (755 ≈ 752): ✔️

[YiFei: Changed commit message to show stats for this patch series]
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/1b61df3db85c5f7f1b9202722c45e7b39df73ef2.1602431034.git.yifeifz2@illinois.edu

+128 -21
+127 -20
tools/testing/selftests/seccomp/seccomp_benchmark.c
··· 4 4 */ 5 5 #define _GNU_SOURCE 6 6 #include <assert.h> 7 + #include <limits.h> 8 + #include <stdbool.h> 9 + #include <stddef.h> 7 10 #include <stdio.h> 8 11 #include <stdlib.h> 9 12 #include <time.h> 10 13 #include <unistd.h> 11 14 #include <linux/filter.h> 12 15 #include <linux/seccomp.h> 16 + #include <sys/param.h> 13 17 #include <sys/prctl.h> 14 18 #include <sys/syscall.h> 15 19 #include <sys/types.h> ··· 74 70 return samples * seconds; 75 71 } 76 72 73 + bool approx(int i_one, int i_two) 74 + { 75 + double one = i_one, one_bump = one * 0.01; 76 + double two = i_two, two_bump = two * 0.01; 77 + 78 + one_bump = one + MAX(one_bump, 2.0); 79 + two_bump = two + MAX(two_bump, 2.0); 80 + 81 + /* Equal to, or within 1% or 2 digits */ 82 + if (one == two || 83 + (one > two && one <= two_bump) || 84 + (two > one && two <= one_bump)) 85 + return true; 86 + return false; 87 + } 88 + 89 + bool le(int i_one, int i_two) 90 + { 91 + if (i_one <= i_two) 92 + return true; 93 + return false; 94 + } 95 + 96 + long compare(const char *name_one, const char *name_eval, const char *name_two, 97 + unsigned long long one, bool (*eval)(int, int), unsigned long long two) 98 + { 99 + bool good; 100 + 101 + printf("\t%s %s %s (%lld %s %lld): ", name_one, name_eval, name_two, 102 + (long long)one, name_eval, (long long)two); 103 + if (one > INT_MAX) { 104 + printf("Miscalculation! Measurement went negative: %lld\n", (long long)one); 105 + return 1; 106 + } 107 + if (two > INT_MAX) { 108 + printf("Miscalculation! Measurement went negative: %lld\n", (long long)two); 109 + return 1; 110 + } 111 + 112 + good = eval(one, two); 113 + printf("%s\n", good ? "✔️" : "❌"); 114 + 115 + return good ? 0 : 1; 116 + } 117 + 77 118 int main(int argc, char *argv[]) 78 119 { 120 + struct sock_filter bitmap_filter[] = { 121 + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), 122 + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 123 + }; 124 + struct sock_fprog bitmap_prog = { 125 + .len = (unsigned short)ARRAY_SIZE(bitmap_filter), 126 + .filter = bitmap_filter, 127 + }; 79 128 struct sock_filter filter[] = { 129 + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, args[0])), 80 130 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 81 131 }; 82 132 struct sock_fprog prog = { 83 133 .len = (unsigned short)ARRAY_SIZE(filter), 84 134 .filter = filter, 85 135 }; 86 - long ret; 87 - unsigned long long samples; 88 - unsigned long long native, filter1, filter2; 136 + 137 + long ret, bits; 138 + unsigned long long samples, calc; 139 + unsigned long long native, filter1, filter2, bitmap1, bitmap2; 140 + unsigned long long entry, per_filter1, per_filter2; 89 141 90 142 printf("Current BPF sysctl settings:\n"); 91 143 system("sysctl net.core.bpf_jit_enable"); ··· 161 101 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 162 102 assert(ret == 0); 163 103 164 - /* One filter */ 104 + /* One filter resulting in a bitmap */ 105 + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 106 + assert(ret == 0); 107 + 108 + bitmap1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 109 + printf("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1); 110 + 111 + /* Second filter resulting in a bitmap */ 112 + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 113 + assert(ret == 0); 114 + 115 + bitmap2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 116 + printf("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2); 117 + 118 + /* Third filter, can no longer be converted to bitmap */ 165 119 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); 166 120 assert(ret == 0); 167 121 168 122 filter1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 169 - printf("getpid RET_ALLOW 1 filter: %llu ns\n", filter1); 123 + printf("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1); 170 124 171 - if (filter1 == native) 172 - printf("No overhead measured!? Try running again with more samples.\n"); 173 - 174 - /* Two filters */ 175 - ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); 125 + /* Fourth filter, can not be converted to bitmap because of filter 3 */ 126 + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 176 127 assert(ret == 0); 177 128 178 129 filter2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 179 - printf("getpid RET_ALLOW 2 filters: %llu ns\n", filter2); 130 + printf("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2); 180 131 181 - /* Calculations */ 182 - printf("Estimated total seccomp overhead for 1 filter: %llu ns\n", 183 - filter1 - native); 132 + /* Estimations */ 133 + #define ESTIMATE(fmt, var, what) do { \ 134 + var = (what); \ 135 + printf("Estimated " fmt ": %llu ns\n", var); \ 136 + if (var > INT_MAX) \ 137 + goto more_samples; \ 138 + } while (0) 184 139 185 - printf("Estimated total seccomp overhead for 2 filters: %llu ns\n", 186 - filter2 - native); 140 + ESTIMATE("total seccomp overhead for 1 bitmapped filter", calc, 141 + bitmap1 - native); 142 + ESTIMATE("total seccomp overhead for 2 bitmapped filters", calc, 143 + bitmap2 - native); 144 + ESTIMATE("total seccomp overhead for 3 full filters", calc, 145 + filter1 - native); 146 + ESTIMATE("total seccomp overhead for 4 full filters", calc, 147 + filter2 - native); 148 + ESTIMATE("seccomp entry overhead", entry, 149 + bitmap1 - native - (bitmap2 - bitmap1)); 150 + ESTIMATE("seccomp per-filter overhead (last 2 diff)", per_filter1, 151 + filter2 - filter1); 152 + ESTIMATE("seccomp per-filter overhead (filters / 4)", per_filter2, 153 + (filter2 - native - entry) / 4); 187 154 188 - printf("Estimated seccomp per-filter overhead: %llu ns\n", 189 - filter2 - filter1); 155 + printf("Expectations:\n"); 156 + ret |= compare("native", "≤", "1 bitmap", native, le, bitmap1); 157 + bits = compare("native", "≤", "1 filter", native, le, filter1); 158 + if (bits) 159 + goto more_samples; 190 160 191 - printf("Estimated seccomp entry overhead: %llu ns\n", 192 - filter1 - native - (filter2 - filter1)); 161 + ret |= compare("per-filter (last 2 diff)", "≈", "per-filter (filters / 4)", 162 + per_filter1, approx, per_filter2); 193 163 164 + bits = compare("1 bitmapped", "≈", "2 bitmapped", 165 + bitmap1 - native, approx, bitmap2 - native); 166 + if (bits) { 167 + printf("Skipping constant action bitmap expectations: they appear unsupported.\n"); 168 + goto out; 169 + } 170 + 171 + ret |= compare("entry", "≈", "1 bitmapped", entry, approx, bitmap1 - native); 172 + ret |= compare("entry", "≈", "2 bitmapped", entry, approx, bitmap2 - native); 173 + ret |= compare("native + entry + (per filter * 4)", "≈", "4 filters total", 174 + entry + (per_filter1 * 4) + native, approx, filter2); 175 + if (ret == 0) 176 + goto out; 177 + 178 + more_samples: 179 + printf("Saw unexpected benchmark result. Try running again with more samples?\n"); 180 + out: 194 181 return 0; 195 182 }
+1 -1
tools/testing/selftests/seccomp/settings
··· 1 - timeout=90 1 + timeout=120