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