162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Strictly speaking, this is not a test. But it can report during test 362306a36Sopenharmony_ci * runs so relative performace can be measured. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define _GNU_SOURCE 662306a36Sopenharmony_ci#include <assert.h> 762306a36Sopenharmony_ci#include <limits.h> 862306a36Sopenharmony_ci#include <stdbool.h> 962306a36Sopenharmony_ci#include <stddef.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <stdlib.h> 1262306a36Sopenharmony_ci#include <time.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <linux/filter.h> 1562306a36Sopenharmony_ci#include <linux/seccomp.h> 1662306a36Sopenharmony_ci#include <sys/param.h> 1762306a36Sopenharmony_ci#include <sys/prctl.h> 1862306a36Sopenharmony_ci#include <sys/syscall.h> 1962306a36Sopenharmony_ci#include <sys/types.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "../kselftest.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciunsigned long long timing(clockid_t clk_id, unsigned long long samples) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct timespec start, finish; 2662306a36Sopenharmony_ci unsigned long long i; 2762306a36Sopenharmony_ci pid_t pid, ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci pid = getpid(); 3062306a36Sopenharmony_ci assert(clock_gettime(clk_id, &start) == 0); 3162306a36Sopenharmony_ci for (i = 0; i < samples; i++) { 3262306a36Sopenharmony_ci ret = syscall(__NR_getpid); 3362306a36Sopenharmony_ci assert(pid == ret); 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci assert(clock_gettime(clk_id, &finish) == 0); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci i = finish.tv_sec - start.tv_sec; 3862306a36Sopenharmony_ci i *= 1000000000ULL; 3962306a36Sopenharmony_ci i += finish.tv_nsec - start.tv_nsec; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci printf("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n", 4262306a36Sopenharmony_ci finish.tv_sec, finish.tv_nsec, 4362306a36Sopenharmony_ci start.tv_sec, start.tv_nsec, 4462306a36Sopenharmony_ci i, (double)i / 1000000000.0); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return i; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciunsigned long long calibrate(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct timespec start, finish; 5262306a36Sopenharmony_ci unsigned long long i, samples, step = 9973; 5362306a36Sopenharmony_ci pid_t pid, ret; 5462306a36Sopenharmony_ci int seconds = 15; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci printf("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci samples = 0; 5962306a36Sopenharmony_ci pid = getpid(); 6062306a36Sopenharmony_ci assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0); 6162306a36Sopenharmony_ci do { 6262306a36Sopenharmony_ci for (i = 0; i < step; i++) { 6362306a36Sopenharmony_ci ret = syscall(__NR_getpid); 6462306a36Sopenharmony_ci assert(pid == ret); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci assert(clock_gettime(CLOCK_MONOTONIC, &finish) == 0); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci samples += step; 6962306a36Sopenharmony_ci i = finish.tv_sec - start.tv_sec; 7062306a36Sopenharmony_ci i *= 1000000000ULL; 7162306a36Sopenharmony_ci i += finish.tv_nsec - start.tv_nsec; 7262306a36Sopenharmony_ci } while (i < 1000000000ULL); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return samples * seconds; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cibool approx(int i_one, int i_two) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci double one = i_one, one_bump = one * 0.01; 8062306a36Sopenharmony_ci double two = i_two, two_bump = two * 0.01; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci one_bump = one + MAX(one_bump, 2.0); 8362306a36Sopenharmony_ci two_bump = two + MAX(two_bump, 2.0); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Equal to, or within 1% or 2 digits */ 8662306a36Sopenharmony_ci if (one == two || 8762306a36Sopenharmony_ci (one > two && one <= two_bump) || 8862306a36Sopenharmony_ci (two > one && two <= one_bump)) 8962306a36Sopenharmony_ci return true; 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cibool le(int i_one, int i_two) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci if (i_one <= i_two) 9662306a36Sopenharmony_ci return true; 9762306a36Sopenharmony_ci return false; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cilong compare(const char *name_one, const char *name_eval, const char *name_two, 10162306a36Sopenharmony_ci unsigned long long one, bool (*eval)(int, int), unsigned long long two) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci bool good; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci printf("\t%s %s %s (%lld %s %lld): ", name_one, name_eval, name_two, 10662306a36Sopenharmony_ci (long long)one, name_eval, (long long)two); 10762306a36Sopenharmony_ci if (one > INT_MAX) { 10862306a36Sopenharmony_ci printf("Miscalculation! Measurement went negative: %lld\n", (long long)one); 10962306a36Sopenharmony_ci return 1; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci if (two > INT_MAX) { 11262306a36Sopenharmony_ci printf("Miscalculation! Measurement went negative: %lld\n", (long long)two); 11362306a36Sopenharmony_ci return 1; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci good = eval(one, two); 11762306a36Sopenharmony_ci printf("%s\n", good ? "✔️" : "❌"); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return good ? 0 : 1; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint main(int argc, char *argv[]) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct sock_filter bitmap_filter[] = { 12562306a36Sopenharmony_ci BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), 12662306a36Sopenharmony_ci BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 12762306a36Sopenharmony_ci }; 12862306a36Sopenharmony_ci struct sock_fprog bitmap_prog = { 12962306a36Sopenharmony_ci .len = (unsigned short)ARRAY_SIZE(bitmap_filter), 13062306a36Sopenharmony_ci .filter = bitmap_filter, 13162306a36Sopenharmony_ci }; 13262306a36Sopenharmony_ci struct sock_filter filter[] = { 13362306a36Sopenharmony_ci BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, args[0])), 13462306a36Sopenharmony_ci BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), 13562306a36Sopenharmony_ci }; 13662306a36Sopenharmony_ci struct sock_fprog prog = { 13762306a36Sopenharmony_ci .len = (unsigned short)ARRAY_SIZE(filter), 13862306a36Sopenharmony_ci .filter = filter, 13962306a36Sopenharmony_ci }; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci long ret, bits; 14262306a36Sopenharmony_ci unsigned long long samples, calc; 14362306a36Sopenharmony_ci unsigned long long native, filter1, filter2, bitmap1, bitmap2; 14462306a36Sopenharmony_ci unsigned long long entry, per_filter1, per_filter2; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci setbuf(stdout, NULL); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci printf("Running on:\n"); 14962306a36Sopenharmony_ci system("uname -a"); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci printf("Current BPF sysctl settings:\n"); 15262306a36Sopenharmony_ci /* Avoid using "sysctl" which may not be installed. */ 15362306a36Sopenharmony_ci system("grep -H . /proc/sys/net/core/bpf_jit_enable"); 15462306a36Sopenharmony_ci system("grep -H . /proc/sys/net/core/bpf_jit_harden"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (argc > 1) 15762306a36Sopenharmony_ci samples = strtoull(argv[1], NULL, 0); 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci samples = calibrate(); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci printf("Benchmarking %llu syscalls...\n", samples); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Native call */ 16462306a36Sopenharmony_ci native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 16562306a36Sopenharmony_ci printf("getpid native: %llu ns\n", native); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 16862306a36Sopenharmony_ci assert(ret == 0); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* One filter resulting in a bitmap */ 17162306a36Sopenharmony_ci ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 17262306a36Sopenharmony_ci assert(ret == 0); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci bitmap1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 17562306a36Sopenharmony_ci printf("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Second filter resulting in a bitmap */ 17862306a36Sopenharmony_ci ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 17962306a36Sopenharmony_ci assert(ret == 0); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci bitmap2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 18262306a36Sopenharmony_ci printf("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Third filter, can no longer be converted to bitmap */ 18562306a36Sopenharmony_ci ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); 18662306a36Sopenharmony_ci assert(ret == 0); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci filter1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 18962306a36Sopenharmony_ci printf("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Fourth filter, can not be converted to bitmap because of filter 3 */ 19262306a36Sopenharmony_ci ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); 19362306a36Sopenharmony_ci assert(ret == 0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci filter2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; 19662306a36Sopenharmony_ci printf("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Estimations */ 19962306a36Sopenharmony_ci#define ESTIMATE(fmt, var, what) do { \ 20062306a36Sopenharmony_ci var = (what); \ 20162306a36Sopenharmony_ci printf("Estimated " fmt ": %llu ns\n", var); \ 20262306a36Sopenharmony_ci if (var > INT_MAX) \ 20362306a36Sopenharmony_ci goto more_samples; \ 20462306a36Sopenharmony_ci } while (0) 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ESTIMATE("total seccomp overhead for 1 bitmapped filter", calc, 20762306a36Sopenharmony_ci bitmap1 - native); 20862306a36Sopenharmony_ci ESTIMATE("total seccomp overhead for 2 bitmapped filters", calc, 20962306a36Sopenharmony_ci bitmap2 - native); 21062306a36Sopenharmony_ci ESTIMATE("total seccomp overhead for 3 full filters", calc, 21162306a36Sopenharmony_ci filter1 - native); 21262306a36Sopenharmony_ci ESTIMATE("total seccomp overhead for 4 full filters", calc, 21362306a36Sopenharmony_ci filter2 - native); 21462306a36Sopenharmony_ci ESTIMATE("seccomp entry overhead", entry, 21562306a36Sopenharmony_ci bitmap1 - native - (bitmap2 - bitmap1)); 21662306a36Sopenharmony_ci ESTIMATE("seccomp per-filter overhead (last 2 diff)", per_filter1, 21762306a36Sopenharmony_ci filter2 - filter1); 21862306a36Sopenharmony_ci ESTIMATE("seccomp per-filter overhead (filters / 4)", per_filter2, 21962306a36Sopenharmony_ci (filter2 - native - entry) / 4); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci printf("Expectations:\n"); 22262306a36Sopenharmony_ci ret |= compare("native", "≤", "1 bitmap", native, le, bitmap1); 22362306a36Sopenharmony_ci bits = compare("native", "≤", "1 filter", native, le, filter1); 22462306a36Sopenharmony_ci if (bits) 22562306a36Sopenharmony_ci goto more_samples; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret |= compare("per-filter (last 2 diff)", "≈", "per-filter (filters / 4)", 22862306a36Sopenharmony_ci per_filter1, approx, per_filter2); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci bits = compare("1 bitmapped", "≈", "2 bitmapped", 23162306a36Sopenharmony_ci bitmap1 - native, approx, bitmap2 - native); 23262306a36Sopenharmony_ci if (bits) { 23362306a36Sopenharmony_ci printf("Skipping constant action bitmap expectations: they appear unsupported.\n"); 23462306a36Sopenharmony_ci goto out; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ret |= compare("entry", "≈", "1 bitmapped", entry, approx, bitmap1 - native); 23862306a36Sopenharmony_ci ret |= compare("entry", "≈", "2 bitmapped", entry, approx, bitmap2 - native); 23962306a36Sopenharmony_ci ret |= compare("native + entry + (per filter * 4)", "≈", "4 filters total", 24062306a36Sopenharmony_ci entry + (per_filter1 * 4) + native, approx, filter2); 24162306a36Sopenharmony_ci if (ret == 0) 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cimore_samples: 24562306a36Sopenharmony_ci printf("Saw unexpected benchmark result. Try running again with more samples?\n"); 24662306a36Sopenharmony_ciout: 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 249