├── .gitignore ├── eval_ipc_ht.sh ├── eval_futex.sh ├── eval_ipc_rt.sh ├── basic_test.c ├── eval_posix.sh ├── eval_hrtimer_rbt.sh ├── Makefile ├── modules ├── helper.h ├── Makefile └── helper.c ├── print_hrtimer.py ├── LICENSE ├── helper.h ├── print_hist.py ├── cacheutils.h ├── eval.py ├── futex_hash_table.c ├── ipc_ids_key_ht.c ├── ipc_ids_ipcs_idr_root_rt.c ├── hrtimer_bases_clock_base_active.c ├── README.md └── posix_timers_hashtable.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.elf 3 | *.ko 4 | *.mod 5 | *.o 6 | *.d 7 | *.order 8 | *.cmd 9 | *.symvers 10 | *.mod.c 11 | .vscode 12 | -------------------------------------------------------------------------------- /eval_ipc_ht.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo ipc hash table with software-agnostic amplification 4 | ./ipc_ids_key_ht.elf 1 7 ipc_ht_amp.csv &> /dev/null 5 | sleep 2 6 | echo ipc hash table without software-agnostic amplification 7 | ./ipc_ids_key_ht.elf 0 6 ipc_ht_no_amp.csv &> /dev/null -------------------------------------------------------------------------------- /eval_futex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo futex hash table with software-agnostic amplification 4 | ./futex_hash_table.elf 1 7 futex_ht_amp.csv &> /dev/null 5 | sleep 2 6 | echo futex hash table without software-agnostic amplification 7 | ./futex_hash_table.elf 0 6 futex_ht_no_amp.csv &> /dev/null -------------------------------------------------------------------------------- /eval_ipc_rt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo ipc radix tree with software-agnostic amplification 4 | ./ipc_ids_ipcs_idr_root_rt.elf 1 7 ipc_rt_amp.csv &> /dev/null 5 | sleep 2 6 | echo ipc radix tree without software-agnostic amplification 7 | ./ipc_ids_ipcs_idr_root_rt.elf 0 6 ipc_rt_no_amp.csv &> /dev/null -------------------------------------------------------------------------------- /basic_test.c: -------------------------------------------------------------------------------- 1 | #include "cacheutils.h" 2 | #include "helper.h" 3 | int main(int argc, char **argv) 4 | { 5 | int fd = open("/dev/helper", O_RDWR); 6 | if (fd < 0) { 7 | printf("[!] kernel module not included\n"); 8 | exit(-1); 9 | } 10 | printf("[+] basic test passed\n"); 11 | } -------------------------------------------------------------------------------- /eval_posix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo posix timer hash table with software-agnostic amplification 4 | ./posix_timers_hashtable.elf 1 7 posix_ht_amp.csv &> /dev/null 5 | sleep 2 6 | echo posix timer hash table without software-agnostic amplification 7 | ./posix_timers_hashtable.elf 0 6 posix_ht_no_amp.csv &> /dev/null -------------------------------------------------------------------------------- /eval_hrtimer_rbt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo hrtimer red-black tree with software-agnostic amplification 4 | ./hrtimer_bases_clock_base_active.elf 1 7 hrtimer_rbt_amp.csv &> /dev/null 5 | sleep 2 6 | echo hrtimer red-black tree without software-agnostic amplification 7 | ./hrtimer_bases_clock_base_active.elf 0 7 hrtimer_rbt_no_amp.csv &> /dev/null -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # object files 2 | CC := gcc 3 | SOURCES := $(wildcard *.c) 4 | TARGETS := $(SOURCES:.c=.elf) 5 | CFLAGS += -g 6 | CFLAGS += -O2 7 | CFLAGS += -Wall 8 | CFLAGS += -lrt 9 | CFLAGS += -pthread 10 | 11 | all: $(TARGETS) 12 | 13 | %.elf: %.c cacheutils.h helper.h 14 | $(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@ 15 | 16 | clean: 17 | rm -f *.elf 18 | -------------------------------------------------------------------------------- /modules/helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TIMER_READ_CNT_HASH 12 4 | #define TIMER_READ_INDEX_HASH 13 5 | #define KEY_READ_CNT 14 6 | #define FUTEX_READ_CNT 15 7 | 8 | typedef union { 9 | struct timer_rd { 10 | size_t timer_id; 11 | size_t uaddr; 12 | } timer_rd; 13 | struct key_rd { 14 | size_t key_id; 15 | size_t uaddr; 16 | } key_rd; 17 | struct futex_rd { 18 | size_t futex_addr; 19 | size_t uaddr; 20 | } futex_rd; 21 | } msg_t; -------------------------------------------------------------------------------- /modules/Makefile: -------------------------------------------------------------------------------- 1 | # kernel module 2 | obj-m := helper.o 3 | KVERSION := $(shell uname -r) 4 | 5 | all: modules remove insert changeperm 6 | 7 | init: modules insert changeperm 8 | 9 | insert: 10 | sudo insmod helper.ko 11 | 12 | changeperm: 13 | sudo chmod 666 /proc/timer_list && sudo chmod 666 /dev/helper 14 | 15 | remove: 16 | sudo rmmod helper.ko 17 | 18 | modules: 19 | make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) modules 20 | 21 | clean: 22 | make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) clean 23 | -------------------------------------------------------------------------------- /print_hrtimer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import csv 4 | import argparse 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | 8 | parser = argparse.ArgumentParser(description="Prints the improvements for hrtimers") 9 | parser.add_argument("filename_no_amp", help="Name of the csv file without amp") 10 | parser.add_argument("filename_amp", help="Name of the csv file with amp") 11 | 12 | args = parser.parse_args() 13 | with open(args.filename_no_amp, "r") as f: 14 | csv_reader = csv.reader(f, delimiter=";") 15 | row = list(csv_reader)[1:] 16 | data_no_amp = {int(r[0]): int(r[1]) for r in row} 17 | min_no_amp = min(data_no_amp.keys()) 18 | with open(args.filename_amp, "r") as f: 19 | csv_reader = csv.reader(f, delimiter=";") 20 | row = list(csv_reader)[1:] 21 | data_amp = {int(r[0]): int(r[1]) for r in row} 22 | min_amp = min(data_amp.keys()) 23 | 24 | plt.plot(data_no_amp.values()) 25 | plt.plot(data_amp.values()) 26 | plt.show() 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Lukas Maar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _GNU_SOURCE 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "modules/helper.h" 11 | 12 | static int fd; 13 | 14 | int timer_read_count_hash(size_t uaddr, size_t timer_id) 15 | { 16 | msg_t msg = { 17 | .timer_rd = { 18 | .timer_id = timer_id, 19 | .uaddr = uaddr, 20 | } 21 | }; 22 | return ioctl(fd, TIMER_READ_CNT_HASH, (unsigned long)&msg); 23 | } 24 | 25 | int timer_read_index_hash(size_t uaddr, size_t timer_id) 26 | { 27 | msg_t msg = { 28 | .timer_rd = { 29 | .timer_id = timer_id, 30 | .uaddr = uaddr, 31 | } 32 | }; 33 | return ioctl(fd, TIMER_READ_INDEX_HASH, (unsigned long)&msg); 34 | } 35 | 36 | int key_read_index(size_t uaddr, size_t key_id) 37 | { 38 | msg_t msg = { 39 | .key_rd = { 40 | .key_id = key_id, 41 | .uaddr = uaddr, 42 | } 43 | }; 44 | return ioctl(fd, KEY_READ_CNT, (unsigned long)&msg); 45 | } 46 | 47 | int futex_read_count(size_t uaddr, size_t futex_addr) 48 | { 49 | msg_t msg = { 50 | .futex_rd = { 51 | .futex_addr = futex_addr, 52 | .uaddr = uaddr, 53 | } 54 | }; 55 | return ioctl(fd, FUTEX_READ_CNT, (unsigned long)&msg); 56 | } 57 | 58 | void helper_init(void) 59 | { 60 | fd = open("/dev/helper", O_RDWR); 61 | if (fd < 0) { 62 | perror("open: "); 63 | exit(-1); 64 | } 65 | } -------------------------------------------------------------------------------- /print_hist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import csv 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import os 7 | import argparse 8 | 9 | def plot_histogram(filename, data, counts): 10 | legend = [] 11 | for i in range(min(counts),10): 12 | d = data[counts == i] 13 | if len(d) == 0: 14 | continue 15 | max_value = np.max(d) 16 | min_value = np.min(d) 17 | bins = max_value-min_value 18 | if bins == 0: 19 | bins = 1 20 | plt.hist(d, bins=bins) 21 | legend.append(str(i)) 22 | i += 1 23 | plt.legend(legend) 24 | plt.title(filename) 25 | plt.show() 26 | 27 | def plot(filename): 28 | with open(filename, "r") as csv_file: 29 | csv_reader = csv.reader(csv_file, delimiter=";") 30 | row = list(csv_reader)[1:] 31 | data = [[int(r[0]), int(r[1])] for r in row] 32 | counts = np.array([d[0] for d in data]) 33 | data = np.array([d[1] for d in data]) 34 | plot_histogram(filename, data, counts) 35 | 36 | def plot_all(): 37 | files = [f for f in os.listdir("./") if ".csv" in f] 38 | for f in files: 39 | print("file {}".format(f)) 40 | with open(f, "r") as csv_file: 41 | csv_reader = csv.reader(csv_file, delimiter=";") 42 | row = list(csv_reader)[1:] 43 | data = [[int(r[0]), int(r[1])] for r in row] 44 | counts = np.array([d[0] for d in data]) 45 | data = np.array([d[1] for d in data]) 46 | plot_histogram(f, data, counts) 47 | 48 | parser = argparse.ArgumentParser(description="Prints histogramm of csv file") 49 | parser.add_argument("-f", "--filename", help="Name of the csv file", default="") 50 | args = parser.parse_args() 51 | 52 | if args.filename == "": 53 | plot_all() 54 | else: 55 | plot(args.filename) -------------------------------------------------------------------------------- /cacheutils.h: -------------------------------------------------------------------------------- 1 | #ifndef CACHEUTILS_H 2 | #define CACHEUTILS_H 3 | 4 | #define _GNU_SOURCE 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef HIDEMINMAX 12 | #define MAX(X,Y) (((X) > (Y)) ? (X) : (Y)) 13 | #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) 14 | #endif 15 | 16 | void pin_to_core(size_t core) 17 | { 18 | int ret; 19 | cpu_set_t cpuset; 20 | 21 | CPU_ZERO(&cpuset); 22 | CPU_SET(core, &cpuset); 23 | 24 | ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); 25 | if (ret) { 26 | perror("sched_setaffinity: "); 27 | exit(-1); 28 | } 29 | } 30 | 31 | void reset_cpu_pin(void) 32 | { 33 | cpu_set_t cpuset; 34 | memset(&cpuset, 0xff, sizeof(cpu_set_t)); 35 | int ret; 36 | ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); 37 | if (ret) { 38 | perror("sched_setaffinity: "); 39 | exit(-1); 40 | } 41 | } 42 | 43 | #define INTEL 44 | // #define AMD 45 | 46 | #define RDPRU ".byte 0x0f, 0x01, 0xfd" 47 | #define RDPRU_ECX_MPERF 0 48 | #define RDPRU_ECX_APERF 1 49 | 50 | static inline size_t rdtsc_begin(void) 51 | { 52 | #if defined(INTEL) 53 | size_t a, d; 54 | asm volatile ("mfence"); 55 | asm volatile ("rdtsc" : "=a" (a), "=d" (d)); 56 | a = (d<<32) | a; 57 | asm volatile ("lfence"); 58 | return a; 59 | #elif defined(AMD) 60 | unsigned long low_a, high_a; 61 | asm volatile ("mfence"); 62 | asm volatile(RDPRU 63 | : "=a" (low_a), "=d" (high_a) 64 | : "c" (RDPRU_ECX_APERF)); 65 | unsigned long aval = ((low_a) | (high_a) << 32); 66 | asm volatile ("lfence"); 67 | return aval; 68 | #endif 69 | } 70 | 71 | static inline size_t rdtsc_end(void) 72 | { 73 | #if defined(INTEL) 74 | size_t a, d; 75 | asm volatile ("lfence"); 76 | asm volatile ("rdtsc" : "=a" (a), "=d" (d)); 77 | a = (d<<32) | a; 78 | asm volatile ("mfence"); 79 | return a; 80 | #elif defined(AMD) 81 | unsigned long low_a, high_a; 82 | asm volatile ("lfence"); 83 | asm volatile(RDPRU 84 | : "=a" (low_a), "=d" (high_a) 85 | : "c" (RDPRU_ECX_APERF)); 86 | unsigned long aval = ((low_a) | (high_a) << 32); 87 | asm volatile ("mfence"); 88 | return aval; 89 | #endif 90 | } 91 | 92 | static inline void flush(__attribute__((unused))size_t p) 93 | { 94 | asm volatile (".intel_syntax noprefix"); 95 | asm volatile ("clflush qword ptr [%0]\n" : : "r" (p)); 96 | asm volatile (".att_syntax"); 97 | } 98 | 99 | static inline void prefetch(__attribute__((unused))size_t p) 100 | { 101 | asm volatile (".intel_syntax noprefix"); 102 | asm volatile ("prefetchnta qword ptr [%0]" : : "r" (p)); 103 | asm volatile ("prefetcht2 qword ptr [%0]" : : "r" (p)); 104 | asm volatile (".att_syntax"); 105 | } 106 | 107 | static inline void longnop(void) 108 | { 109 | asm volatile ("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 110 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 111 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 112 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 113 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 114 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 115 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 116 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"); 117 | } 118 | #endif 119 | -------------------------------------------------------------------------------- /eval.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import csv 4 | import numpy as np 5 | 6 | def eval(filename, gt0, gt1): 7 | with open(filename, "r") as f: 8 | csv_reader = csv.reader(f, delimiter=";") 9 | row = list(csv_reader)[1:] 10 | data = np.array([[int(r[0]), int(r[1])] for r in row]) 11 | data0 = [d[1] for d in data if d[0] == int(gt0)] 12 | data1 = [d[1] for d in data if d[0] == int(gt1)] 13 | mean0 = np.median(data0) 14 | mean1 = np.median(data1) 15 | threshold = (mean1 + mean0)/2 16 | fp = np.count_nonzero(data1 < threshold) 17 | fn = np.count_nonzero(data0 > threshold) 18 | p = (fp+fn)/(len(data)) 19 | # print("data0.median() {: 5.1f}".format(mean0)) 20 | # print("data1.median() {: 5.1f}".format(mean1)) 21 | # print("threshold {: 5.1f}".format(threshold)) 22 | # print("data0 {: 5d}".format(np.count_nonzero(data0))) 23 | # print("data1 {: 5d}".format(np.count_nonzero(data1))) 24 | # print("false positives {: 5d} {: 3.1f}%".format(fp, 100*fp/np.count_nonzero(data1))) 25 | # print("false negatives {: 5d} {: 3.1f}%".format(fn, 100*fn/np.count_nonzero(data0))) 26 | fp=100*fp/np.count_nonzero(data1) 27 | fn=100*fn/np.count_nonzero(data0) 28 | return fp,fn,p 29 | 30 | ''' 31 | futex_ht_no_amp.csv 32 | futex_ht_amp.csv 33 | posix_ht_no_amp.csv 34 | posix_ht_amp.csv 35 | ipc_ht_no_amp.csv 36 | ipc_ht_amp.csv 37 | ipc_rt_no_amp.csv 38 | ipc_rt_amp.csv 39 | timer_rbt_no_amp.csv 40 | timer_rbt_amp.csv 41 | ''' 42 | def eval_print(name, filename0, filename1): 43 | fp0,fn0,_ = eval(filename0, 0, 1) 44 | fp1,fn1,_ = eval(filename0, 0, 3) 45 | fp2,fn2,_ = eval(filename1, 0, 1) 46 | fp3,fn3,_ = eval(filename1, 0, 3) 47 | if fn0 != 0: 48 | fn_reduce = 100.0*(1-fn3/fn0) 49 | else: 50 | fn_reduce = 100.0 51 | if fp0 != 0: 52 | fp_reduce = 100.0*(1-fp3/fp0) 53 | else: 54 | fp_reduce = 100.0 55 | fp0 = "{:5.1f}".format(fp0) 56 | fn0 = "{:5.1f}".format(fn0) 57 | fp1 = "{:5.1f}".format(fp1) 58 | fn1 = "{:5.1f}".format(fn1) 59 | fp2 = "{:5.1f}".format(fp2) 60 | fn2 = "{:5.1f}".format(fn2) 61 | fp3 = "{:5.1f}".format(fp3) 62 | fn3 = "{:5.1f}".format(fn3) 63 | fp_reduce = "{:4.0f}".format(fp_reduce) 64 | fn_reduce = "{:4.0f}".format(fn_reduce) 65 | print("| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |".format(name, fp0, fn0, fp1, fn1, fp2, fn2, fp3, fn3, fp_reduce, fn_reduce)) 66 | pass 67 | def eval_print1(name, filename0, filename1): 68 | fp0,fn0,_ = eval(filename0, 0, 1) 69 | fp2,fn2,_ = eval(filename1, 0, 1) 70 | if fn0 != 0: 71 | fn_reduce = 100*(1-fn2/fn0) 72 | else: 73 | fn_reduce = 100.0 74 | if fp0 != 0: 75 | fp_reduce = 100*(1-fp2/fp0) 76 | else: 77 | fp_reduce = 100.0 78 | fp0 = "{:5.1f}".format(fp0) 79 | fn0 = "{:5.1f}".format(fn0) 80 | fp2 = "{:5.1f}".format(fp2) 81 | fn2 = "{:5.1f}".format(fn2) 82 | fp_reduce = "{:4.0f}".format(fp_reduce) 83 | fn_reduce = "{:4.0f}".format(fn_reduce) 84 | print("| {} | {} | {} | | | {} | {} | | | {} | {} |".format(name, fp0, fn0, fp2, fn2, fp_reduce, fn_reduce)) 85 | pass 86 | 87 | print("-"*105) 88 | print("| Container instance | W/o struct-agnostic | W struct-agnostic | Reduction |") 89 | print("| | 1 to 0 | 3 to 0 | 1 to 0 | 3 to 0 | |") 90 | print("| | FPR | FNR | FPR | FNR | FPR | FNR | FPR | FNR | |") 91 | print("-"*105) 92 | eval_print("posix timers hash table", "posix_ht_no_amp.csv", "posix_ht_amp.csv") 93 | eval_print("futex hash table ", "futex_ht_no_amp.csv", "futex_ht_amp.csv") 94 | eval_print("ipc hash table ", "ipc_ht_no_amp.csv", "ipc_ht_amp.csv") 95 | eval_print1("ipc radix tree ", "ipc_rt_no_amp.csv", "ipc_rt_amp.csv") 96 | print("-"*105) -------------------------------------------------------------------------------- /futex_hash_table.c: -------------------------------------------------------------------------------- 1 | #include "cacheutils.h" 2 | #include "helper.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define TOTAL_FUTEXES 4096 14 | #define LOOP 2 15 | #define FUTEXES 2048 16 | #define REPEAT_MEASUREMENT 512 17 | #define AVERAGE (1<<3) 18 | pthread_t tids[FUTEXES*LOOP]; 19 | unsigned int probe_futexes[TOTAL_FUTEXES]; 20 | volatile unsigned int futexes[FUTEXES*LOOP*4096]; 21 | 22 | static int futex(unsigned int *uaddr, int futex_op, unsigned int val, const struct timespec *timeout, unsigned int *uaddr2, unsigned int val3) 23 | { 24 | return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); 25 | } 26 | void *do_job(void *arg) 27 | { 28 | int ret; 29 | size_t id = (size_t)arg; 30 | id = (id*4096) | (id*8 % 4096); 31 | ret = futex((unsigned int *)&futexes[id], FUTEX_WAIT_PRIVATE, 0, NULL, NULL, 0); 32 | if (ret < 0) { 33 | perror("futex do_job"); 34 | exit(-1); 35 | } 36 | return 0; 37 | } 38 | int compare(const void *a, const void *b) 39 | { 40 | return (*(size_t *)a - *(size_t *)b); 41 | } 42 | 43 | void print_usage(char *name) 44 | { 45 | printf("%s \n", name); 46 | exit(0); 47 | } 48 | 49 | char *name = 0; 50 | size_t software_agnostic_amplification = 0; 51 | size_t core = 7; 52 | int main(int argc, char **argv) 53 | { 54 | static volatile char flush_cache[(1<<10)*(1<<7)]; 55 | if (argc > 4) 56 | print_usage(argv[0]); 57 | software_agnostic_amplification = (argc > 1) && strcmp(argv[1], "0"); 58 | if (argc > 2) 59 | core = atoi(argv[2]); 60 | if (argc > 3) 61 | name = argv[3]; 62 | int ret; 63 | size_t ground_truths[TOTAL_FUTEXES*LOOP] = {0}; 64 | size_t times[TOTAL_FUTEXES*LOOP]; 65 | pin_to_core(core); 66 | helper_init(); 67 | printf("[*] warmup\n"); 68 | for (volatile size_t i = 0; i < 0x400000000; ++i); 69 | printf("[*] start\n"); 70 | for (size_t j = 0; j < LOOP; ++j) { 71 | printf("[*] %zd/%d\n", j, LOOP); 72 | for (size_t i = 0; i < FUTEXES; ++i) { 73 | size_t id = i+FUTEXES*j; 74 | ret = pthread_create(&tids[id], 0, do_job, (void *)id); 75 | if (ret < 0) { 76 | perror("pthread_create"); 77 | exit(-1); 78 | } 79 | } 80 | for (size_t i = 0; i < TOTAL_FUTEXES; ++i) { 81 | size_t t0; 82 | size_t t1; 83 | size_t time = 0; 84 | size_t __times[REPEAT_MEASUREMENT]; 85 | for (size_t l = 0; l < REPEAT_MEASUREMENT; ++l) { 86 | sched_yield(); 87 | if (software_agnostic_amplification) 88 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 89 | t0 = rdtsc_begin(); 90 | ret = futex(&probe_futexes[i], FUTEX_WAKE_PRIVATE, 0x10, NULL, NULL, 0); 91 | t1 = rdtsc_end(); 92 | __times[l] = t1 - t0; 93 | if (ret < 0) { 94 | perror("futex main"); 95 | exit(-1); 96 | } 97 | } 98 | qsort(__times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 99 | for (size_t l = 0; l < AVERAGE; ++l) 100 | time += __times[l]; 101 | time /= AVERAGE; 102 | times[i+j*TOTAL_FUTEXES] = time; 103 | futex_read_count((size_t)&ground_truths[i+j*TOTAL_FUTEXES], (size_t)&probe_futexes[i]); 104 | } 105 | sleep(1); 106 | } 107 | for (size_t i = 0; i < TOTAL_FUTEXES*LOOP; ++i) { 108 | if (i % 16 == 0) 109 | printf("[%4zd] ", i); 110 | printf("%4zd (%zd)", times[i], ground_truths[i]); 111 | if (i % 16 == 15) 112 | printf("\n"); 113 | } 114 | if (name == 0) { 115 | printf("[?] save to file? [y/N] "); 116 | char in = getchar(); 117 | if (in != 'y') 118 | exit(0); 119 | } 120 | 121 | time_t current_time; 122 | struct tm *timeinfo; 123 | char buffer[256]; 124 | 125 | time(¤t_time); 126 | timeinfo = localtime(¤t_time); 127 | strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo); 128 | 129 | if (name == 0) { 130 | static char file_name[0x1000]; 131 | snprintf(file_name, sizeof(file_name), "futex_hash_table_%s.csv", buffer); 132 | name = file_name; 133 | } 134 | FILE *csv_file = fopen(name, "w"); 135 | 136 | if (csv_file == NULL) { 137 | printf("[!] fopen\n"); 138 | return 1; 139 | } 140 | 141 | // Write headers 142 | fprintf(csv_file, "ground truth; time\n"); 143 | 144 | for (size_t i = 0; i < TOTAL_FUTEXES*LOOP; ++i) { 145 | fprintf(csv_file, "%ld; %ld\n", ground_truths[i], times[i]); 146 | } 147 | 148 | fclose(csv_file); 149 | printf("[+] done\n"); 150 | } 151 | -------------------------------------------------------------------------------- /ipc_ids_key_ht.c: -------------------------------------------------------------------------------- 1 | #include "cacheutils.h" 2 | #include "helper.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define KEY_MEASURE 0xbadc0fe 13 | #define KEY_START 0xdeadbeef 14 | #define KEY_WARMUP 0xbadbabe 15 | 16 | #define REPEAT_MEASUREMENT 1024 17 | #define AVERAGE (1<<3) 18 | #define OFFSET 2 19 | #define KEYS 8*1024 20 | 21 | #define TRIES 8 22 | 23 | #define WARMUP_KEYS 4096 24 | size_t msgiqs[KEYS]; 25 | 26 | int compare(const void *a, const void *b) 27 | { 28 | return (*(size_t *)a - *(size_t *)b); 29 | } 30 | 31 | void cleanup(void) 32 | { 33 | printf("[*] cleanup\n"); 34 | for (size_t i = 0; i < KEYS; ++i) { 35 | msgctl(msgiqs[i], IPC_RMID, NULL); 36 | } 37 | } 38 | 39 | void print_usage(char *name) 40 | { 41 | printf("%s \n", name); 42 | exit(0); 43 | } 44 | 45 | char *name = 0; 46 | size_t software_agnostic_amplification = 0; 47 | size_t core = 7; 48 | int main(int argc, char **argv) 49 | { 50 | static volatile char flush_cache[(1<<10)*(1<<7)]; 51 | if (argc > 4) 52 | print_usage(argv[0]); 53 | software_agnostic_amplification = (argc > 1) && strcmp(argv[1], "0"); 54 | if (argc > 2) 55 | core = atoi(argv[2]); 56 | if (argc > 3) 57 | name = argv[3]; 58 | 59 | helper_init(); 60 | pin_to_core(core); 61 | 62 | printf("[*] warmup\n"); 63 | for (volatile size_t i = 0; i < 0x400000000; ++i); 64 | 65 | int ret; 66 | size_t times[KEYS]; 67 | size_t times_std[KEYS]; 68 | size_t ground_truth[KEYS]; 69 | // size_t warmup_msgiqs[WARMUP_KEYS]; 70 | // printf("[*] allocate warmup keys\n"); 71 | // for (size_t i = 0; i < WARMUP_KEYS; ++i) { 72 | // ret = msgget(KEY_WARMUP+i, IPC_CREAT); 73 | // if (ret < 0) { 74 | // printf("[!] i %ld\n", i); 75 | // perror("msgget(KEY_WARMUP+i, IPC_CREAT)"); 76 | // exit(-1); 77 | // } 78 | // warmup_msgiqs[i] = ret; 79 | // } 80 | 81 | printf("[*] start measure\n"); 82 | for (size_t i = 0; i < KEYS; ++i) { 83 | if (i % 16 == 0) 84 | printf("[*] %ld/%d\n", i, KEYS); 85 | 86 | ret = msgget(KEY_START+i, IPC_CREAT); 87 | if (ret < 0) { 88 | printf("[!] i %ld\n", i); 89 | perror("msgget(KEY_START+i, IPC_CREAT)"); 90 | cleanup(); 91 | exit(-1); 92 | } 93 | msgiqs[i] = ret; 94 | 95 | size_t tries = TRIES; 96 | redo: 97 | if (tries-- == 0) { 98 | printf("[!] no more tries\n"); 99 | cleanup(); 100 | exit(-1); 101 | } 102 | size_t t0; 103 | size_t t1; 104 | size_t time = 0; 105 | size_t measured_times[REPEAT_MEASUREMENT]; 106 | for (size_t j = 0; j < REPEAT_MEASUREMENT; ++j) { 107 | sched_yield(); 108 | if (software_agnostic_amplification) 109 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 110 | t0 = rdtsc_begin(); 111 | ret = msgget(KEY_MEASURE+i, 0); 112 | // ret = msgget(KEY_START+i, IPC_CREAT | IPC_EXCL); 113 | t1 = rdtsc_end(); 114 | measured_times[j] = t1 - t0; 115 | if (ret != -1) { 116 | printf("[!] i %ld ret %d\n", i, ret); 117 | perror("msgget(KEY_MEASURE+i, 0)"); 118 | // perror("msgget(KEY_START+i, IPC_CREAT | IPC_EXCL)"); 119 | cleanup(); 120 | exit(-1); 121 | } 122 | } 123 | qsort(measured_times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 124 | for (size_t i = 0; i < AVERAGE; ++i) 125 | time += measured_times[OFFSET+i]; 126 | time /= AVERAGE; 127 | times[i] = time; 128 | times_std[i] = measured_times[AVERAGE-1+OFFSET] - measured_times[OFFSET]; 129 | if (times_std[i] > 20/*2*/) { 130 | printf("[*] %ld/%d\n", tries, TRIES); 131 | goto redo; 132 | } 133 | ret = key_read_index((size_t)&ground_truth[i], KEY_MEASURE+i); 134 | if (ret < 0) { 135 | printf("[!] key_read_index error %d\n", ret); 136 | cleanup(); 137 | exit(-1); 138 | } 139 | } 140 | 141 | printf("[*] print results\n"); 142 | for (size_t i = 0; i < KEYS; ++i) { 143 | if (i % 16 == 0) 144 | printf("[%4zd] ", i); 145 | // printf("%4zd(%3zd) [%zd]", times[i], times_std[i], ground_truth[i]); 146 | printf("%4zd [%zd]", times[i], ground_truth[i]); 147 | if (i % 16 == 15) 148 | printf("\n"); 149 | } 150 | 151 | cleanup(); 152 | 153 | if (name == 0) { 154 | printf("[?] save to file? [y/N] "); 155 | char in = getchar(); 156 | if (in != 'y') 157 | exit(0); 158 | } 159 | 160 | time_t current_time; 161 | struct tm *timeinfo; 162 | char buffer[80]; 163 | 164 | time(¤t_time); 165 | timeinfo = localtime(¤t_time); 166 | strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo); 167 | 168 | if (name == 0) { 169 | static char file_name[0x1000]; 170 | snprintf(file_name, sizeof(file_name), "ipc_ids_key_ht_%s.csv", buffer); 171 | name = file_name; 172 | } 173 | FILE *csv_file = fopen(name, "w"); 174 | 175 | if (csv_file == NULL) { 176 | printf("[!] fopen\n"); 177 | return 1; 178 | } 179 | 180 | // Write headers 181 | fprintf(csv_file, "ground truth; time\n"); 182 | 183 | for (size_t i = 0; i < KEYS; ++i) { 184 | fprintf(csv_file, "%ld; %ld\n", ground_truth[i], times[i]); 185 | } 186 | 187 | fclose(csv_file); 188 | printf("[+] done\n"); 189 | 190 | return 0; 191 | } 192 | -------------------------------------------------------------------------------- /ipc_ids_ipcs_idr_root_rt.c: -------------------------------------------------------------------------------- 1 | #include "cacheutils.h" 2 | #include "helper.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define KEY_MEASURE (1<<6) 13 | #define KEY_START 0 14 | 15 | #define REPEAT_MEASUREMENT 512 16 | #define AVERAGE (1<<3) 17 | #define OFFSET 2 18 | #define KEYS 1024 19 | 20 | #define TRIES 8 21 | 22 | #define WARMUP_KEYS 4096 23 | size_t msgiqs[KEYS]; 24 | 25 | int compare(const void *a, const void *b) 26 | { 27 | return (*(size_t *)a - *(size_t *)b); 28 | } 29 | 30 | void cleanup(void) 31 | { 32 | printf("[*] cleanup\n"); 33 | for (size_t i = 0; i < KEYS; ++i) { 34 | msgctl(msgiqs[i], IPC_RMID, NULL); 35 | } 36 | } 37 | 38 | void print_usage(char *name) 39 | { 40 | printf("%s \n", name); 41 | exit(0); 42 | } 43 | 44 | char *name = 0; 45 | size_t software_agnostic_amplification = 0; 46 | size_t core = 7; 47 | int main(int argc, char **argv) 48 | { 49 | static volatile char flush_cache[(1<<10)*(1<<7)]; 50 | if (argc > 4) 51 | print_usage(argv[0]); 52 | software_agnostic_amplification = (argc > 1) && strcmp(argv[1], "0"); 53 | if (argc > 2) 54 | core = atoi(argv[2]); 55 | if (argc > 3) 56 | name = argv[3]; 57 | 58 | 59 | helper_init(); 60 | pin_to_core(core); 61 | 62 | printf("[*] warmup\n"); 63 | for (volatile size_t i = 0; i < 0x400000000; ++i); 64 | 65 | int ret; 66 | size_t ground_truths[KEYS]; 67 | size_t times[KEYS]; 68 | size_t times_std[KEYS]; 69 | 70 | printf("[*] start measure\n"); 71 | for (size_t i = 0; i < KEYS; ++i) { 72 | if (i % 8 == 0) 73 | printf("[*] %ld/%d\n", i, KEYS); 74 | 75 | ret = msgget(KEY_START+i, IPC_CREAT); 76 | if (ret < 0) { 77 | printf("[!] i %ld\n", i); 78 | perror("msgget(KEY_START+i, IPC_CREAT)"); 79 | cleanup(); 80 | exit(-1); 81 | } 82 | msgiqs[i] = ret; 83 | 84 | size_t tries = TRIES; 85 | redo: 86 | if (tries-- == 0) { 87 | printf("[!] no more tries\n"); 88 | cleanup(); 89 | exit(-1); 90 | } 91 | size_t t0; 92 | size_t t1; 93 | size_t time = 0; 94 | size_t measured_times[REPEAT_MEASUREMENT]; 95 | for (size_t j = 0; j < REPEAT_MEASUREMENT; ++j) { 96 | sched_yield(); 97 | if (software_agnostic_amplification) 98 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 99 | t0 = rdtsc_begin(); 100 | ret = msgctl(KEY_MEASURE, IPC_STAT, NULL); 101 | t1 = rdtsc_end(); 102 | measured_times[j] = t1 - t0; 103 | if (ret != -1) { 104 | printf("[!] i %ld ret %d\n", i, ret); 105 | perror("msgctl(KEY_MEASURE, 0)"); 106 | cleanup(); 107 | exit(-1); 108 | } 109 | } 110 | qsort(measured_times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 111 | for (size_t i = 0; i < AVERAGE; ++i) 112 | time += measured_times[OFFSET+i]; 113 | time /= AVERAGE; 114 | times[i] = time; 115 | times_std[i] = measured_times[AVERAGE-1+OFFSET] - measured_times[OFFSET]; 116 | if (times_std[i] > 20/*2*/) { 117 | printf("[*] %ld/%d\n", tries, TRIES); 118 | goto redo; 119 | } 120 | 121 | ground_truths[i] = 0; 122 | if (i < 64) 123 | continue; 124 | msgctl(msgiqs[i], IPC_RMID, NULL); 125 | ground_truths[i] = 1; 126 | i++; 127 | 128 | tries = TRIES; 129 | redo2: 130 | if (tries-- == 0) { 131 | printf("[!] no more tries\n"); 132 | cleanup(); 133 | exit(-1); 134 | } 135 | time = 0; 136 | for (size_t j = 0; j < REPEAT_MEASUREMENT; ++j) { 137 | sched_yield(); 138 | if (software_agnostic_amplification) 139 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 140 | t0 = rdtsc_begin(); 141 | ret = msgctl(KEY_MEASURE, IPC_STAT, NULL); 142 | t1 = rdtsc_end(); 143 | measured_times[j] = t1 - t0; 144 | if (ret != -1) { 145 | printf("[!] i %ld ret %d\n", i, ret); 146 | perror("msgctl(KEY_MEASURE, 0)"); 147 | cleanup(); 148 | exit(-1); 149 | } 150 | } 151 | qsort(measured_times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 152 | for (size_t i = 0; i < AVERAGE; ++i) 153 | time += measured_times[OFFSET+i]; 154 | time /= AVERAGE; 155 | times[i] = time; 156 | times_std[i] = measured_times[AVERAGE-1+OFFSET] - measured_times[OFFSET]; 157 | if (times_std[i] > 20/*2*/) { 158 | printf("[*] %ld/%d\n", tries, TRIES); 159 | goto redo2; 160 | } 161 | } 162 | 163 | printf("[*] print results\n"); 164 | for (size_t i = 0; i < KEYS; ++i) { 165 | if (i % 32 == 0) 166 | printf("[%5zd] ", i); 167 | printf("%4zd ", times[i]); 168 | if (i % 32 == 31) 169 | printf("\n"); 170 | } 171 | 172 | cleanup(); 173 | 174 | if (name == 0) { 175 | printf("[?] save to file? [y/N] "); 176 | char in = getchar(); 177 | if (in != 'y') 178 | exit(0); 179 | } 180 | 181 | time_t current_time; 182 | struct tm *timeinfo; 183 | char buffer[256]; 184 | 185 | time(¤t_time); 186 | timeinfo = localtime(¤t_time); 187 | strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo); 188 | 189 | if (name == 0) { 190 | static char file_name[0x1000]; 191 | snprintf(file_name, sizeof(file_name), "ipc_ids_ipcs_idr_root_rt_%s.csv", buffer); 192 | name = file_name; 193 | } 194 | FILE *csv_file = fopen(name, "w"); 195 | 196 | if (csv_file == NULL) { 197 | printf("[!] fopen\n"); 198 | return 1; 199 | } 200 | 201 | // Write headers 202 | fprintf(csv_file, "ground truth; time\n"); 203 | 204 | for (size_t i = 0; i < KEYS; ++i) { 205 | fprintf(csv_file, "%ld; %ld\n", ground_truths[i], times[i]); 206 | } 207 | 208 | fclose(csv_file); 209 | printf("[+] done\n"); 210 | 211 | return 0; 212 | } 213 | -------------------------------------------------------------------------------- /hrtimer_bases_clock_base_active.c: -------------------------------------------------------------------------------- 1 | #include "cacheutils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define REPEAT_MEASUREMENT 1024 14 | #define AVERAGE (1<<3) 15 | #define OFFSET 2 16 | #define TIMERS 1024 17 | #define TRIES 8 18 | 19 | int fd_timer_list; 20 | size_t get_ground_truth(size_t cpu) 21 | { 22 | static char buffer[1<<30]; 23 | char *running = buffer; 24 | size_t size = sizeof(buffer); 25 | int ret; 26 | lseek(fd_timer_list, 0, SEEK_SET); 27 | for (size_t i = 0; i < 10; ++i) { 28 | ret = read(fd_timer_list, running, size); 29 | if (ret < 0) 30 | break; 31 | running += ret; 32 | size -= ret; 33 | } 34 | 35 | char start[100]; 36 | snprintf(start, sizeof(start), "cpu: %ld", cpu); 37 | 38 | char *str = strstr(buffer, start); 39 | if (str == 0) { 40 | printf("[!] strstr(buffer, \"cpu: CPU\")\n"); 41 | exit(-1); 42 | } 43 | str = strstr(str, "active timers:"); 44 | if (str == 0) { 45 | printf("[!] strstr(buffer, \"active timers\")\n"); 46 | exit(-1); 47 | } 48 | 49 | char *res = 0; 50 | char *pch = strtok(str, "\n"); 51 | while (pch != NULL) { 52 | if (strstr(pch, "clock 1:") != 0) 53 | break; 54 | char *tmp = strstr(pch, "expires"); 55 | if (tmp == 0) 56 | res = pch; 57 | // printf("%s\n", pch); 58 | pch = strtok(NULL, "\n"); 59 | } 60 | res++; 61 | res++; 62 | char *tmp = res; 63 | while (1) { 64 | if (*tmp == ':') { 65 | *tmp = '\0'; 66 | break; 67 | } 68 | tmp++; 69 | } 70 | // printf(res); 71 | return atoi(res); 72 | } 73 | 74 | int compare(const void *a, const void *b) 75 | { 76 | return (*(size_t *)a - *(size_t *)b); 77 | } 78 | 79 | void print_usage(char *name) 80 | { 81 | printf("%s \n", name); 82 | exit(0); 83 | } 84 | 85 | char *name = 0; 86 | size_t software_agnostic_amplification = 0; 87 | size_t core = 7; 88 | int main(int argc, char **argv) 89 | { 90 | static volatile char flush_cache[(1<<10)*(1<<7)]; 91 | if (argc > 4) 92 | print_usage(argv[0]); 93 | software_agnostic_amplification = (argc > 1) && strcmp(argv[1], "0"); 94 | if (argc > 2) 95 | core = atoi(argv[2]); 96 | if (argc > 3) 97 | name = argv[3]; 98 | 99 | 100 | int ret; 101 | struct rlimit l = { 102 | .rlim_cur = 131072, 103 | .rlim_max = 131072, 104 | }; 105 | ret = setrlimit(RLIMIT_NOFILE, &l); 106 | if (ret < 0) { 107 | perror("setrlimit"); 108 | exit(-1); 109 | } 110 | 111 | fd_timer_list = open("/proc/timer_list", O_RDONLY); 112 | if (fd_timer_list < 0) { 113 | perror("open(/proc/timer_list)"); 114 | exit(-1); 115 | } 116 | 117 | pin_to_core(core); 118 | printf("[*] warmup\n"); 119 | for (volatile size_t i = 0; i < 0x4000000; ++i); 120 | 121 | size_t times[TIMERS]; 122 | size_t times_std[TIMERS]; 123 | size_t ground_truth[TIMERS]; 124 | 125 | int fd_probe = timerfd_create(CLOCK_REALTIME, 0); 126 | if (fd_probe < 0) { 127 | perror("timerfd_create"); 128 | exit(-1); 129 | } 130 | 131 | struct itimerspec spec = { 132 | { 1000000, 1000000 }, 133 | { 1000000, 1000000 } 134 | }; 135 | 136 | printf("[*] start measure\n"); 137 | for (size_t i = 0; i < TIMERS; ++i) { 138 | if (i % 16 == 0) 139 | printf("[*] %ld/%d\n", i, TIMERS); 140 | 141 | int fd = timerfd_create(CLOCK_REALTIME, 0); 142 | if (fd < 0) { 143 | perror("timerfd_create"); 144 | exit(-1); 145 | } 146 | int ret = timerfd_settime(fd, 0, &spec, NULL); 147 | if (ret < 0) { 148 | perror("timerfd_settime"); 149 | exit(-1); 150 | } 151 | 152 | size_t tries = TRIES; 153 | redo: 154 | if (tries-- == 0) { 155 | printf("[!] no more tries\n"); 156 | exit(-1); 157 | } 158 | size_t t0; 159 | size_t t1; 160 | size_t time = 0; 161 | size_t measured_times[REPEAT_MEASUREMENT]; 162 | for (size_t j = 0; j < REPEAT_MEASUREMENT; ++j) { 163 | sched_yield(); 164 | if (software_agnostic_amplification) 165 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 166 | t0 = rdtsc_begin(); 167 | int ret = timerfd_settime(fd_probe, 0, &spec, NULL); 168 | t1 = rdtsc_end(); 169 | measured_times[j] = t1 - t0; 170 | if (ret < 0) { 171 | printf("[!] i %ld ret %d\n", i, ret); 172 | perror("timerfd_settime"); 173 | exit(-1); 174 | } 175 | } 176 | qsort(measured_times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 177 | for (size_t i = 0; i < AVERAGE; ++i) 178 | time += measured_times[OFFSET+i]; 179 | time /= AVERAGE; 180 | times[i] = time; 181 | times_std[i] = measured_times[AVERAGE-1+OFFSET] - measured_times[OFFSET]; 182 | if (times_std[i] > 5) { 183 | printf("[*] %ld/%d\n", tries, TRIES); 184 | goto redo; 185 | } 186 | ground_truth[i] = get_ground_truth(core); 187 | } 188 | 189 | printf("[*] print results\n"); 190 | for (size_t i = 0; i < TIMERS; ++i) { 191 | if (i % 16 == 0) 192 | printf("[%4zd] ", i); 193 | printf("%4zd [%4zd]", times[i], ground_truth[i]); 194 | if (i % 16 == 15) 195 | printf("\n"); 196 | } 197 | 198 | if (name == 0) { 199 | printf("[?] save to file? [y/N] "); 200 | char in = getchar(); 201 | if (in != 'y') 202 | exit(0); 203 | } 204 | 205 | time_t current_time; 206 | struct tm *timeinfo; 207 | char buffer[80]; 208 | 209 | time(¤t_time); 210 | timeinfo = localtime(¤t_time); 211 | strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo); 212 | 213 | if (name == 0) { 214 | static char file_name[0x1000]; 215 | snprintf(file_name, sizeof(file_name), "hrtimer_bases_clock_base_active_%s.csv", buffer); 216 | name = file_name; 217 | } 218 | FILE *csv_file = fopen(name, "w"); 219 | 220 | if (csv_file == NULL) { 221 | printf("[!] fopen\n"); 222 | return 1; 223 | } 224 | 225 | // Write headers 226 | fprintf(csv_file, "ground truth; time\n"); 227 | 228 | for (size_t i = 0; i < TIMERS; ++i) { 229 | fprintf(csv_file, "%ld; %ld\n", ground_truth[i], times[i]); 230 | } 231 | 232 | fclose(csv_file); 233 | printf("[+] done\n"); 234 | 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KernelSnitch Artifacts 2 | 3 | This repo contains artifacts developed during a research project, as well as the code to perform KernelSnitch, which got accepted at NDSS '25. 4 | 5 | ## What is KernelSnitch 6 | 7 | KernelSnitch is a novel software-induced side-channel attack that targets kernel data container structures such as hash tables and trees. These structures vary in size and access time depending on the number of elements they hold, i.e., the occupancy level. KernelSnitch exploits this variability to constitute a timing side channel that is exploitable to an unprivileged, isolated attacker from user space. Despite the small timing differences relative to system call runtime, we demonstrate methods to reliably amplify these timing variations for successful exploitation. 8 | 9 | The artifacts demonstrate the timing side channel and show the practicality of distinguishing between different occupancy levels. We provide a kernel module and execution scripts for evaluation. While our timing side channel is software induced, we recommend evaluation on hardware similar to ours (i.e., Intel i7-1260P, i7-1165G7, i7-12700, and Xeon Gold 6530) to reproduce similar results as in our paper. While the attacks should work generically on Linux kernels, we recommend to evaluate the artifacts on downstream Ubuntu Linux kernels v5.15, v6.5, or v6.8, as these are the versions we primarily evaluate. For the timing side channel, the evaluation shows that the occupancy level of data container structures can be leaked by measuring the timing of syscalls that access these structures. 10 | 11 | ## Description & Requirements 12 | 13 | ### Security, Privacy, and Ethical Concerns 14 | 15 | The artifacts do not perform any destructive steps, as we only show the timing side channel to leak the occupancy level of data container structures and exclude the case studies, i.e., secretly transmitting data via a covert channel, leaking kernel heap pointers and monitoring user activity via website fingerprinting. 16 | 17 | ### How to Access 18 | 19 | We provide the source code ([github](https://github.com/IAIK/KernelSnitch/)) for performing the timing side channel. 20 | 21 | ### Hardware Dependencies 22 | 23 | While the timing side channel is software induced, one of the amplification methods depends on hardware buffers, i.e., the CPU caches. We have evaluated the Intel i7-1260P, i7-1165G7, i7-12700 and Xeon Gold 6530. We therefore expect similar results with similar processors. 24 | 25 | ### Software Dependencies 26 | 27 | While the attacks should generically work on Linux kernels, we recommend evaluating the artifacts on a downstream Ubuntu Linux kernel v5.15, v6.5, or v6.8. For reference, our primary evaluation system was Ubuntu 22.04 with generic kernels v5.15, v6.5, or v6.8. 28 | 29 | One part of the artifact evaluation is to insert a kernel module that requires *root privileges*. This module is required to obtain the ground truth of the occupancy level of kernel data structures. We tested our kernel module with the downstream Ubuntu Linux kernels v5.15, v6.5, and v6.8. For other kernels that have different config files (or other downstream changes) our implemented module may not do what we intended. Specifically, in order to obtain the occupancy ground truth of the data structures, we redefined and reimplemented several structures and functions according to how they are implemented in the Ubuntu Linux kernel. We did this because several functions used to access kernel data structures are implemented as inline functions, e.g., `__rhashtable_lookup`, which prevented us from calling these functions directly. Another reason is that several structs, e.g., `msg_queue`, are defined in c files, which also prevents us from using these struct definitions. 30 | 31 | ## Artifact Installation & Configuration 32 | 33 | ### Installation 34 | 35 | The installation required to perform the artifact evaluation works as following: 36 | 37 | - Clone our [github repository](https://github.com/IAIK/KernelSnitch/) to the `/repo/path` directory. 38 | - Change directory to `/repo/path/modules`. 39 | - Execute `make init` to build and insert the kernel module. 40 | - Change directory to `/repo/path`. 41 | - Select in `/repo/path/cacheutils.h` either the `INTEL` or `AMD` macro depending on your system. 42 | - Execute `make` to build all experiment binaries. 43 | 44 | ### Basic Tests 45 | 46 | Testing the basic functionality works as following: 47 | 48 | - Change directory to `/repo/path`. 49 | - Execute `./basic_test.elf` should print `[+] basic test passed`. 50 | 51 | ## Evaluation 52 | 53 | As described in Section V-B **External Noise**, the most dominant noise source is CPU frequency fluctuation. Therefore, perform the following experiments with as little background activity as possible to reproduce the figures from the paper. We even suggest to perform the experiments on an idle system with no other activity. 54 | 55 | #### POSIX timer hash table experiment (E1) 56 | 57 | _How to:_ 58 | Execute `./posix_timers_hashtable.elf `, with `` is a boolean which performs the experiment with/without structure-agnostic amplification, `` pins the process to the specific core, and `` stores the results in this file. For convenience, we provide the `eval_posix.sh` script which internally executes `posix_timers_hashtable.elf` with and without structure-agnostic amplification. To reproduce Figure 8, execute `./print_hist.py -f `, where `` is either `posix_ht_amp.csv` or `posix_ht_no_amp.csv`. 59 | 60 | _Preparation:_ 61 | Do **Installation**. 62 | 63 | _Execution:_ 64 | `./eval_posix.sh` and `./print_hist.py -f `, where `` is either `posix_ht_amp.csv` or `posix_ht_no_amp.csv`. 65 | 66 | _Results:_ 67 | `print_hist.py` should reproduce Figure 8. 68 | 69 | 70 | #### Futex hash table experiment (E2) 71 | 72 | _How to:_ 73 | Same as for (E1) but with `futex_hash_table.elf` and `eval_futex.sh`. 74 | 75 | _Preparation:_ 76 | Do **Installation**. 77 | 78 | _Execution:_ 79 | `./eval_futex.sh` and `./print_hist.py -f `, where `` is either `futex_ht_amp.csv` or `futex_ht_no_amp.csv`. 80 | 81 | _Results:_ 82 | `print_hist.py` should reproduce Figure 9. 83 | 84 | 85 | #### IPC hash table experiment (E3) 86 | 87 | _How to:_ 88 | Same as for (E1) but with `ipc_ids_key_ht.elf` and `eval_ipc_ht.sh`. 89 | 90 | _Preparation:_ 91 | Do **Installation**. 92 | 93 | _Execution:_ 94 | `./eval_ipc_ht.sh` and `./print_hist.py -f `, where `` is either `ipc_ht_amp.csv` or `ipc_ht_no_amp.csv`. 95 | 96 | _Results:_ 97 | `print_hist.py` should reproduce Figure 10. 98 | 99 | 100 | #### IPC radix tree experiment (E4) 101 | 102 | _How to:_ 103 | Same as for (E1) but with `ipc_ids_ipcs_idr_root_rt.elf` and `eval_ipc_rt.sh`. 104 | 105 | _Preparation:_ 106 | Do **Installation**. 107 | 108 | _Execution:_ 109 | `./eval_ipc_rt.sh` and and `./print_hist.py -f `, where `` is either `ipc_rt_amp.csv` or `ipc_rt_no_amp.csv`. 110 | 111 | _Results:_ 112 | `print_hist.py` should reproduce Figure 11. 113 | 114 | 115 | #### Hrtimer red-black tree experiment (E5) 116 | 117 | _How to:_ 118 | Similar as for (E1) but with `hrtimer_bases_clock_base_active.elf` and `eval_hrtimer_rbt.sh`, and `print_hrtimer.py`. 119 | 120 | _Preparation:_ 121 | Do **Installation**. 122 | 123 | _Execution:_ 124 | `./eval_hrtimer_rbt.sh` and `./print_hrtimer.py hrtimer_rbt_no_amp.csv hrtimer_rbt_amp.csv`. 125 | 126 | _Results:_ 127 | `print_hrtimer.py` should reproduce Figure 12. 128 | 129 | 130 | #### Amplification Improvement experiment (E6) 131 | 132 | _How to:_ 133 | Execute `eval.py` prints similar results to Table I in ASCII form. 134 | 135 | _Preparation:_ 136 | Do **Installation** and experiments (E1-5). 137 | 138 | _Execution:_ 139 | `./eval.py`. 140 | 141 | _Results:_ 142 | `eval.py` should reproduce Table I. -------------------------------------------------------------------------------- /posix_timers_hashtable.c: -------------------------------------------------------------------------------- 1 | // #define MEMSET 2 | #include "cacheutils.h" 3 | #include "helper.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define TOTAL_TIMERS 512 19 | #define LOOP 8 20 | #define TIMERS 256 21 | 22 | int __create_timer(void) 23 | { 24 | int ret = 0; 25 | size_t timer_id = 0; 26 | ret = timer_create(0, 0, (timer_t *)&timer_id); 27 | if (ret < 0) { 28 | perror("timer_create: "); 29 | return -1; 30 | } 31 | return (int)timer_id; 32 | } 33 | void __delete_timer(size_t timer_id) 34 | { 35 | int ret = 0; 36 | ret = timer_delete((timer_t)timer_id); 37 | if (ret != 0) { 38 | perror("timer_delete: "); 39 | exit(-1); 40 | } 41 | } 42 | 43 | void __prime_timer(size_t timer_id) 44 | { 45 | int ret = timer_gettime((timer_t)timer_id, 0); 46 | if (ret == 0) { 47 | perror("timer_gettime: "); 48 | exit(-1); 49 | } 50 | } 51 | int compare(const void *a, const void *b) 52 | { 53 | return (*(size_t *)a - *(size_t *)b); 54 | } 55 | #define REPEAT_MEASUREMENT 512//(1<<6) 56 | #define AVERAGE (1<<3) 57 | size_t software_agnostic_amplification = 0; 58 | size_t prime_timer(size_t timer_id) 59 | { 60 | static volatile char flush_cache[(1<<10)*(1<<7)]; 61 | size_t t0; 62 | size_t t1; 63 | size_t time = 0; 64 | size_t times[REPEAT_MEASUREMENT]; 65 | for (size_t i = 0; i < REPEAT_MEASUREMENT; ++i) { 66 | sched_yield(); 67 | if (software_agnostic_amplification) 68 | memset((char *)flush_cache, 1, sizeof(flush_cache)); 69 | t0 = rdtsc_begin(); 70 | __prime_timer(timer_id); 71 | t1 = rdtsc_end(); 72 | times[i] = t1 - t0; 73 | } 74 | qsort(times, REPEAT_MEASUREMENT, sizeof(size_t), compare); 75 | for (size_t i = 0; i < AVERAGE; ++i) 76 | time += times[i]; 77 | time /= AVERAGE; 78 | return time; 79 | } 80 | 81 | #define INDEX (1024*1024-1) // is invalid as no timer can have this index 82 | void __print_ground_truth(size_t with_score) 83 | { 84 | int ret; 85 | size_t count; 86 | size_t counts[512]; 87 | for (size_t i = 0; i < 512; ++i) { 88 | ret = timer_read_count_hash((size_t)&count, INDEX+i); 89 | if (ret != 0) { 90 | printf("[!] timer %zd error\n", i); 91 | exit(-1); 92 | } 93 | counts[i] = count; 94 | } 95 | size_t highest_count = 0; 96 | for (size_t i = 0; i < 512; ++i) { 97 | if (counts[i] > highest_count) { 98 | highest_count = counts[i]; 99 | } 100 | } 101 | for (size_t i = 0; i < 512; ++i) { 102 | if (i % 32 == 0) 103 | printf("[%4zd] ", i); 104 | if (with_score == 1 && counts[i] == highest_count) 105 | printf("\033[1;31m"); 106 | printf("%4zd", counts[i]); 107 | if (with_score == 1 && counts[i] == highest_count) 108 | printf("\033[0m"); 109 | if (i % 32 == 31) 110 | printf("\n"); 111 | } 112 | } 113 | void print_ground_truth(void) 114 | { 115 | __print_ground_truth(0); 116 | } 117 | void print_ground_truth_score(void) 118 | { 119 | __print_ground_truth(1); 120 | } 121 | void __print_side_channel(size_t with_score) 122 | { 123 | size_t times[512]; 124 | for (size_t i = 0; i < 512; ++i) 125 | times[i] = prime_timer(INDEX+i); 126 | size_t highest_time = 0; 127 | for (size_t i = 0; i < 512; ++i) { 128 | if (times[i] > highest_time) { 129 | highest_time = times[i]; 130 | } 131 | } 132 | for (size_t i = 0; i < 512; ++i) { 133 | if (i % 32 == 0) 134 | printf("[%4zd] ", i); 135 | if (with_score == 1 && times[i] == highest_time) 136 | printf("\033[1;31m"); 137 | printf("%4zd", times[i]); 138 | if (with_score == 1 && times[i] == highest_time) 139 | printf("\033[0m"); 140 | if (i % 32 == 31) 141 | printf("\n"); 142 | } 143 | } 144 | void print_side_channel(void) 145 | { 146 | __print_side_channel(0); 147 | } 148 | void print_side_channel_score(void) 149 | { 150 | __print_side_channel(1); 151 | } 152 | void print_side_channel_and_ground_truth(void) 153 | { 154 | int ret; 155 | size_t ground_truth; 156 | size_t ground_truths[512]; 157 | size_t times[512]; 158 | for (size_t i = 0; i < 512; ++i) { 159 | times[i] = prime_timer(INDEX+i); 160 | ret = timer_read_count_hash((size_t)&ground_truth, INDEX+i); 161 | if (ret != 0) { 162 | printf("[!] timer %zd error\n", i); 163 | exit(-1); 164 | } 165 | ground_truths[i] = ground_truth; 166 | } 167 | for (size_t i = 0; i < 512; ++i) { 168 | if (i % 16 == 0) 169 | printf("[%4zd] ", i); 170 | printf("%4zd (%zd)", times[i], ground_truths[i]); 171 | if (i % 16 == 15) 172 | printf("\n"); 173 | } 174 | } 175 | 176 | size_t read_hash(size_t timer_id) 177 | { 178 | int ret; 179 | size_t hash; 180 | ret = timer_read_index_hash((size_t)&hash, timer_id); 181 | if (ret != 0) { 182 | printf("[!] timer_read_index_hash %zd error\n", timer_id); 183 | exit(-1); 184 | } 185 | return hash; 186 | } 187 | 188 | void print_usage(char *name) 189 | { 190 | printf("%s \n", name); 191 | exit(0); 192 | } 193 | 194 | char *name = 0; 195 | size_t core = 7; 196 | int main(int argc, char **argv) 197 | { 198 | if (argc > 4) 199 | print_usage(argv[0]); 200 | software_agnostic_amplification = (argc > 1) && strcmp(argv[1], "0"); 201 | if (argc > 2) 202 | core = atoi(argv[2]); 203 | if (argc > 3) 204 | name = argv[3]; 205 | 206 | helper_init(); 207 | pin_to_core(core); 208 | 209 | int ret; 210 | size_t ground_truths[TOTAL_TIMERS*LOOP]; 211 | size_t times[TOTAL_TIMERS*LOOP]; 212 | printf("[*] warmup\n"); 213 | for (volatile size_t i = 0; i < 0x100000000; ++i); 214 | print_side_channel_and_ground_truth(); 215 | printf("[*] start\n"); 216 | for (size_t j = 0; j < LOOP; ++j) { 217 | printf("[*] %zd/%d\n", j, LOOP); 218 | for (size_t i = 0; i < TIMERS; ++i) 219 | __create_timer(); 220 | size_t ground_truth; 221 | for (size_t i = 0; i < TOTAL_TIMERS; ++i) { 222 | times[i+j*TOTAL_TIMERS] = prime_timer(INDEX+i); 223 | ret = timer_read_count_hash((size_t)&ground_truth, INDEX+i); 224 | if (ret != 0) { 225 | printf("[!] timer %zd error\n", i); 226 | exit(-1); 227 | } 228 | ground_truths[i+j*TOTAL_TIMERS] = ground_truth; 229 | } 230 | } 231 | for (size_t i = 0; i < TOTAL_TIMERS*LOOP; ++i) { 232 | if (i % 32 == 0) 233 | printf("[%4zd] ", i); 234 | printf("%4zd (%zd)", times[i], ground_truths[i]); 235 | if (i % 32 == 31) 236 | printf("\n"); 237 | } 238 | 239 | if (name == 0) { 240 | printf("[?] save to file? [y/N] "); 241 | char in = getchar(); 242 | if (in != 'y') 243 | exit(0); 244 | } 245 | 246 | time_t current_time; 247 | struct tm *timeinfo; 248 | char buffer[256]; 249 | 250 | time(¤t_time); 251 | timeinfo = localtime(¤t_time); 252 | strftime(buffer, sizeof(buffer), "%Y%m%d_%H%M%S", timeinfo); 253 | 254 | if (name == 0) { 255 | static char file_name[0x1000]; 256 | snprintf(file_name, sizeof(file_name), "posix_timers_hashtable_%s.csv", buffer); 257 | name = file_name; 258 | } 259 | FILE *csv_file = fopen(name, "w"); 260 | 261 | if (csv_file == NULL) { 262 | printf("[!] fopen\n"); 263 | return 1; 264 | } 265 | 266 | // Write headers 267 | fprintf(csv_file, "ground truth; time\n"); 268 | 269 | for (size_t i = 0; i < TOTAL_TIMERS*LOOP; ++i) { 270 | fprintf(csv_file, "%ld; %ld\n", ground_truths[i], times[i]); 271 | } 272 | 273 | fclose(csv_file); 274 | printf("[+] done\n"); 275 | 276 | } 277 | -------------------------------------------------------------------------------- /modules/helper.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "helper.h" 25 | 26 | #define DEBUG_LVL 3 27 | 28 | #if LINUX_VERSION_CODE > KERNEL_VERSION(6, 6, 0) 29 | #define plist_for_each_entry_safe(pos, n, head, m) \ 30 | list_for_each_entry_safe(pos, n, &(head)->node_list, m.node_list) 31 | #endif 32 | 33 | #if DEBUG_LVL > 3 34 | #define debug_debug(FMT, ...) do {printk(KERN_DEBUG "[%-5.5s%5u] " FMT, current->comm, current->pid, ##__VA_ARGS__);} while (0) 35 | #else 36 | #define debug_debug(FMT, ...) do {} while (0) 37 | #endif 38 | 39 | #if DEBUG_LVL > 2 40 | #define debug_info(FMT, ...) do {printk(KERN_INFO "[%-5.5s%5u] " FMT, current->comm, current->pid, ##__VA_ARGS__);} while (0) 41 | #else 42 | #define debug_info(FMT, ...) do {} while (0) 43 | #endif 44 | 45 | #if DEBUG_LVL > 1 46 | #define debug_alert(FMT, ...) do {printk(KERN_ALERT "[%-5.5s%5u] " FMT, current->comm, current->pid, ##__VA_ARGS__);} while (0) 47 | #else 48 | #define debug_alert(FMT, ...) do {} while (0) 49 | #endif 50 | 51 | #if DEBUG_LVL > 0 52 | #define debug_error(FMT, ...) do {printk(KERN_ERR "[%-5.5s%5u] " FMT, current->comm, current->pid, ##__VA_ARGS__);} while (0) 53 | #else 54 | #define debug_error(FMT, ...) do {} while (0) 55 | #endif 56 | 57 | #define DEVICE_NAME "helper" 58 | #define CLASS_NAME "helperclass" 59 | 60 | int helper_init_device_driver(void); 61 | static int helper_init(void); 62 | static void helper_cleanup(void); 63 | int helper_open(struct inode *inode, struct file *filp); 64 | int helper_release(struct inode *inode, struct file *filep); 65 | long helper_ioctl(struct file *file, unsigned int num, long unsigned int param); 66 | 67 | static struct file_operations helper_fops = { 68 | .owner = THIS_MODULE, 69 | .open = helper_open, 70 | .release = helper_release, 71 | .unlocked_ioctl = helper_ioctl, 72 | }; 73 | 74 | static int helper_major; 75 | static struct class *helper_class; 76 | static struct device *helper_device; 77 | 78 | static struct hlist_head *posix_timers_hashtable; 79 | struct futex_hash_bucket; 80 | union futex_key; 81 | static struct futex_hash_bucket *(*futex_hash)(union futex_key *key); 82 | struct msg_queue; 83 | struct msg_queue *(*ipc_obtain_object_check)(struct ipc_ids *ns, int id); 84 | static struct kprobe kp = { 85 | .symbol_name = "posix_timers_hashtable" 86 | }; 87 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) 88 | static struct kprobe kp1 = { 89 | .symbol_name = "futex_hash" 90 | }; 91 | #else 92 | static struct kprobe kp1 = { 93 | .symbol_name = "hash_futex" 94 | }; 95 | #endif 96 | static struct kprobe kp2 = { 97 | .symbol_name = "__futex_data" 98 | }; 99 | static struct kprobe kp3 = { 100 | .symbol_name = "ipc_obtain_object_check" 101 | }; 102 | struct futex_data { 103 | struct futex_hash_bucket *queues; 104 | unsigned long hashsize; 105 | } __aligned(2*sizeof(long)); 106 | struct futex_data *__futex_data; 107 | #define futex_queues (__futex_data->queues) 108 | #define futex_hashsize (__futex_data->hashsize) 109 | 110 | /* 111 | * Initialization device driver 112 | */ 113 | int helper_init_device_driver(void) 114 | { 115 | int ret; 116 | debug_info("helper:init_device_driver: start\n"); 117 | 118 | ret = register_chrdev(0, DEVICE_NAME, &helper_fops); 119 | if (ret < 0) goto ERROR; 120 | helper_major = ret; 121 | 122 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 4, 0) 123 | helper_class = class_create(THIS_MODULE, CLASS_NAME); 124 | #else 125 | helper_class = class_create(CLASS_NAME); 126 | #endif 127 | if (IS_ERR(helper_class)) { 128 | ret = PTR_ERR(helper_class); 129 | goto ERROR1; 130 | } 131 | 132 | helper_device = device_create(helper_class, 0, MKDEV(helper_major, 0), 0, DEVICE_NAME); 133 | if (IS_ERR(helper_device)) { 134 | ret = PTR_ERR(helper_device); 135 | goto ERROR2; 136 | } 137 | 138 | debug_info("helper:init_device_driver: done '/dev/%s c %d' 0 created\n", DEVICE_NAME, helper_major); 139 | return 0; 140 | 141 | ERROR2: 142 | debug_alert("helper:init_device_driver: class destroy\n"); 143 | class_unregister(helper_class); 144 | class_destroy(helper_class); 145 | ERROR1: 146 | debug_alert("helper:init_device_driver: unregister chrdev\n"); 147 | unregister_chrdev(helper_major, CLASS_NAME); 148 | ERROR: 149 | debug_alert("helper:init_device_driver: fail %d\n", ret); 150 | helper_device = 0; 151 | helper_class = 0; 152 | helper_major = -1; 153 | return ret; 154 | } 155 | 156 | /* 157 | * Initialization 158 | */ 159 | static int helper_init(void) 160 | { 161 | int ret; 162 | debug_info("helper:init: start with kernel version: %d.%d.%d\n", 163 | (LINUX_VERSION_CODE >> 16) & 0xFF, // Major version 164 | (LINUX_VERSION_CODE >> 8) & 0xFF, // Minor version 165 | LINUX_VERSION_CODE & 0xFF); 166 | 167 | ret = helper_init_device_driver(); 168 | if (ret) goto ERROR; 169 | 170 | register_kprobe(&kp); 171 | posix_timers_hashtable = (void *)kp.addr; 172 | register_kprobe(&kp1); 173 | futex_hash = (struct futex_hash_bucket *(*)(union futex_key *))kp1.addr; 174 | register_kprobe(&kp2); 175 | __futex_data = (struct futex_data *)kp2.addr; 176 | register_kprobe(&kp3); 177 | ipc_obtain_object_check = (struct msg_queue *(*)(struct ipc_ids *, int ))kp3.addr; 178 | 179 | debug_info("helper:init: posix_timers_hashtable %016zx\n", (size_t)posix_timers_hashtable); 180 | debug_info("helper:init: futex_hash %016zx\n", (size_t)futex_hash); 181 | debug_info("helper:init: __futex_data %016zx\n", (size_t)__futex_data); 182 | debug_info("helper:init: ipc_obtain_object_check %016zx\n", (size_t)ipc_obtain_object_check); 183 | 184 | debug_info("helper:init: done\n"); 185 | return 0; 186 | 187 | ERROR: 188 | debug_alert("helper:init: error\n"); 189 | return ret; 190 | } 191 | 192 | /* 193 | * Cleanup 194 | */ 195 | static void helper_cleanup(void) 196 | { 197 | debug_info("helper:cleanup\n"); 198 | unregister_kprobe(&kp); 199 | unregister_kprobe(&kp1); 200 | unregister_kprobe(&kp2); 201 | unregister_kprobe(&kp3); 202 | device_destroy(helper_class, MKDEV(helper_major, 0)); 203 | class_unregister(helper_class); 204 | class_destroy(helper_class); 205 | unregister_chrdev(helper_major, DEVICE_NAME); 206 | } 207 | 208 | /* 209 | * Close "/dev/helper" 210 | */ 211 | int helper_release(struct inode *inode, struct file *filep) 212 | { 213 | debug_info("helper:release\n"); 214 | module_put(THIS_MODULE); 215 | return 0; 216 | } 217 | EXPORT_SYMBOL(helper_release); 218 | 219 | /* 220 | * Open "/dev/helper" 221 | */ 222 | int helper_open(struct inode *inode, struct file *filp) 223 | { 224 | debug_info("helper:open\n"); 225 | try_module_get(THIS_MODULE); 226 | return 0; 227 | } 228 | EXPORT_SYMBOL(helper_open); 229 | 230 | /* 231 | * Getting the count (ground truth) for ipc_ids.key_ht 232 | */ 233 | #define IPC_MSG_IDS 1 234 | #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) 235 | static const struct rhashtable_params ipc_kht_params = { 236 | .head_offset = offsetof(struct kern_ipc_perm, khtnode), 237 | .key_offset = offsetof(struct kern_ipc_perm, key), 238 | .key_len = sizeof_field(struct kern_ipc_perm, key), 239 | .automatic_shrinking = true, 240 | }; 241 | static long ___rhashtable_lookup( 242 | struct rhashtable *ht, const void *key, 243 | const struct rhashtable_params params) 244 | { 245 | struct rhashtable_compare_arg arg = { 246 | .ht = ht, 247 | .key = key, 248 | }; 249 | struct rhash_lock_head __rcu *const *bkt; 250 | struct bucket_table *tbl; 251 | struct rhash_head *he; 252 | unsigned int hash; 253 | long count = 0; 254 | 255 | tbl = rht_dereference_rcu(ht->tbl, ht); 256 | restart: 257 | hash = rht_key_hashfn(ht, tbl, key, params); 258 | bkt = rht_bucket(tbl, hash); 259 | do { 260 | rht_for_each_rcu_from(he, rht_ptr_rcu(bkt), tbl, hash) { 261 | if (params.obj_cmpfn ? 262 | params.obj_cmpfn(&arg, rht_obj(ht, he)) : 263 | rhashtable_compare(&arg, rht_obj(ht, he))) { 264 | count++; 265 | continue; 266 | } 267 | return count; 268 | } 269 | /* An object might have been moved to a different hash chain, 270 | * while we walk along it - better check and retry. 271 | */ 272 | } while (he != RHT_NULLS_MARKER(bkt)); 273 | 274 | /* Ensure we see any new tables. */ 275 | smp_rmb(); 276 | 277 | tbl = rht_dereference_rcu(tbl->future_tbl, ht); 278 | if (unlikely(tbl)) 279 | goto restart; 280 | 281 | return count; 282 | } 283 | static long _ipc_findkey(struct ipc_ids *ids, key_t key) 284 | { 285 | return ___rhashtable_lookup(&ids->key_ht, &key, 286 | ipc_kht_params); 287 | } 288 | static long _ksys_msgget(key_t key) 289 | { 290 | return _ipc_findkey(&msg_ids(current->nsproxy->ipc_ns), key); 291 | } 292 | 293 | /* 294 | * Hash for k_itimer 295 | */ 296 | static int hash(struct signal_struct *sig, unsigned int nr) 297 | { 298 | return hash_32(hash32_ptr(sig) ^ nr, 9); 299 | } 300 | 301 | struct futex_hash_bucket { 302 | atomic_t waiters; 303 | spinlock_t lock; 304 | struct plist_head chain; 305 | } ____cacheline_aligned_in_smp; 306 | struct futex_pi_state { 307 | struct list_head list; 308 | struct rt_mutex_base pi_mutex; 309 | struct task_struct *owner; 310 | refcount_t refcount; 311 | union futex_key key; 312 | } __randomize_layout; 313 | struct futex_q { 314 | struct plist_node list; 315 | struct task_struct *task; 316 | spinlock_t *lock_ptr; 317 | void *wake; 318 | void *wake_data; 319 | union futex_key key; 320 | struct futex_pi_state *pi_state; 321 | struct rt_mutex_waiter *rt_waiter; 322 | union futex_key *requeue_pi_key; 323 | u32 bitset; 324 | atomic_t requeue_state; 325 | struct rcuwait requeue_wait; 326 | } __randomize_layout; 327 | static size_t futex_read_count(size_t futex_addr, struct mm_struct *mm) 328 | { 329 | union futex_key key; 330 | struct futex_hash_bucket *hb; 331 | size_t count = 0; 332 | struct futex_q *this; 333 | struct futex_q *next; 334 | 335 | key.private.mm = mm; 336 | key.private.address = futex_addr & ~0xfff; 337 | key.private.offset = futex_addr & 0xfff; 338 | hb = futex_hash(&key); 339 | spin_lock(&hb->lock); 340 | plist_for_each_entry_safe(this, next, &hb->chain, list) { 341 | count++; 342 | } 343 | spin_unlock(&hb->lock); 344 | return count; 345 | } 346 | 347 | struct msg_queue { 348 | struct kern_ipc_perm q_perm; 349 | time64_t q_stime; 350 | time64_t q_rtime; 351 | time64_t q_ctime; 352 | unsigned long q_cbytes; 353 | unsigned long q_qnum; 354 | unsigned long q_qbytes; 355 | struct pid *q_lspid; 356 | struct pid *q_lrpid; 357 | struct list_head q_messages; 358 | struct list_head q_receivers; 359 | struct list_head q_senders; 360 | }; 361 | #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) 362 | 363 | /* 364 | * ioctl code 365 | */ 366 | long helper_ioctl(struct file *_, unsigned int num, long unsigned int param) 367 | { 368 | long ret; 369 | msg_t msg; 370 | size_t *uaddr = 0; 371 | size_t tmp = -1; 372 | struct hlist_head *head; 373 | struct k_itimer *timer; 374 | size_t count = 0; 375 | debug_debug("helper:ioctl: start num 0x%08x param 0x%016lx\n", num, param); 376 | 377 | ret = copy_from_user((msg_t*)&msg, (msg_t*)param, sizeof(msg_t)); 378 | if (ret < 0) { 379 | debug_alert("helper:ioctl: copy_from_user failed\n"); 380 | ret = -1; 381 | goto RETURN; 382 | } 383 | 384 | switch (num) { 385 | case TIMER_READ_CNT_HASH: 386 | head = &posix_timers_hashtable[hash(current->signal, msg.timer_rd.timer_id)]; 387 | hlist_for_each_entry(timer, head, t_hash) { 388 | count++; 389 | } 390 | tmp = count; 391 | uaddr = (size_t *)msg.timer_rd.uaddr; 392 | debug_debug("helper:ioctl: timer with %zd has %zd entries\n", msg.timer_rd.timer_id, count); 393 | goto COPY_TMP_TO_USER; 394 | 395 | case TIMER_READ_INDEX_HASH: 396 | tmp = hash(current->signal, msg.timer_rd.timer_id); 397 | uaddr = (size_t *)msg.timer_rd.uaddr; 398 | debug_debug("helper:ioctl: hash for %zd is %zx\n", msg.timer_rd.timer_id, tmp); 399 | goto COPY_TMP_TO_USER; 400 | 401 | case KEY_READ_CNT: 402 | uaddr = (size_t *)msg.timer_rd.uaddr; 403 | tmp = _ksys_msgget(msg.key_rd.key_id); 404 | debug_debug("helper:ioctl: key with %zd has %zd entries\n", msg.key_rd.key_id, tmp); 405 | goto COPY_TMP_TO_USER; 406 | 407 | case FUTEX_READ_CNT: 408 | tmp = futex_read_count(msg.futex_rd.futex_addr, current->mm); 409 | uaddr = (size_t *)msg.futex_rd.uaddr; 410 | debug_debug("helper:ioctl: futex with address %zd has %zd entries\n", msg.futex_rd.futex_addr, tmp); 411 | goto COPY_TMP_TO_USER; 412 | 413 | default: 414 | debug_alert("helper:ioctl: no valid num\n"); 415 | ret = -1; 416 | goto RETURN; 417 | } 418 | ret = 0; 419 | goto DONE; 420 | COPY_TMP_TO_USER: 421 | BUG_ON(uaddr == 0 && "forgot to set uaddr"); 422 | BUG_ON(tmp == -1 && "forgot to set tmp"); 423 | debug_debug("helper:ioctl: copy 0x%016zx to mem[0x%016zx]\n", tmp, (size_t)uaddr); 424 | ret = copy_to_user(uaddr, &tmp, sizeof(size_t)); 425 | if (ret < 0) { 426 | debug_alert("helper:ioctl: copy_to_user failed\n"); 427 | ret = -1; 428 | goto RETURN; 429 | } 430 | ret = 0; 431 | 432 | DONE: 433 | debug_debug("helper:ioctl: done\n"); 434 | RETURN: 435 | return ret; 436 | } 437 | EXPORT_SYMBOL(helper_ioctl); 438 | 439 | module_init(helper_init); 440 | module_exit(helper_cleanup); 441 | 442 | MODULE_LICENSE("GPL"); 443 | MODULE_AUTHOR("Lukas Maar"); 444 | MODULE_DESCRIPTION("LKM"); 445 | MODULE_VERSION("0.1"); 446 | --------------------------------------------------------------------------------