cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timer.c (7969B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (c) 2021 Facebook */
      3#include <linux/bpf.h>
      4#include <time.h>
      5#include <errno.h>
      6#include <bpf/bpf_helpers.h>
      7#include "bpf_tcp_helpers.h"
      8
      9char _license[] SEC("license") = "GPL";
     10struct hmap_elem {
     11	int counter;
     12	struct bpf_timer timer;
     13	struct bpf_spin_lock lock; /* unused */
     14};
     15
     16struct {
     17	__uint(type, BPF_MAP_TYPE_HASH);
     18	__uint(max_entries, 1000);
     19	__type(key, int);
     20	__type(value, struct hmap_elem);
     21} hmap SEC(".maps");
     22
     23struct {
     24	__uint(type, BPF_MAP_TYPE_HASH);
     25	__uint(map_flags, BPF_F_NO_PREALLOC);
     26	__uint(max_entries, 1000);
     27	__type(key, int);
     28	__type(value, struct hmap_elem);
     29} hmap_malloc SEC(".maps");
     30
     31struct elem {
     32	struct bpf_timer t;
     33};
     34
     35struct {
     36	__uint(type, BPF_MAP_TYPE_ARRAY);
     37	__uint(max_entries, 2);
     38	__type(key, int);
     39	__type(value, struct elem);
     40} array SEC(".maps");
     41
     42struct {
     43	__uint(type, BPF_MAP_TYPE_LRU_HASH);
     44	__uint(max_entries, 4);
     45	__type(key, int);
     46	__type(value, struct elem);
     47} lru SEC(".maps");
     48
     49__u64 bss_data;
     50__u64 err;
     51__u64 ok;
     52__u64 callback_check = 52;
     53__u64 callback2_check = 52;
     54
     55#define ARRAY 1
     56#define HTAB 2
     57#define HTAB_MALLOC 3
     58#define LRU 4
     59
     60/* callback for array and lru timers */
     61static int timer_cb1(void *map, int *key, struct bpf_timer *timer)
     62{
     63	/* increment bss variable twice.
     64	 * Once via array timer callback and once via lru timer callback
     65	 */
     66	bss_data += 5;
     67
     68	/* *key == 0 - the callback was called for array timer.
     69	 * *key == 4 - the callback was called from lru timer.
     70	 */
     71	if (*key == ARRAY) {
     72		struct bpf_timer *lru_timer;
     73		int lru_key = LRU;
     74
     75		/* rearm array timer to be called again in ~35 seconds */
     76		if (bpf_timer_start(timer, 1ull << 35, 0) != 0)
     77			err |= 1;
     78
     79		lru_timer = bpf_map_lookup_elem(&lru, &lru_key);
     80		if (!lru_timer)
     81			return 0;
     82		bpf_timer_set_callback(lru_timer, timer_cb1);
     83		if (bpf_timer_start(lru_timer, 0, 0) != 0)
     84			err |= 2;
     85	} else if (*key == LRU) {
     86		int lru_key, i;
     87
     88		for (i = LRU + 1;
     89		     i <= 100  /* for current LRU eviction algorithm this number
     90				* should be larger than ~ lru->max_entries * 2
     91				*/;
     92		     i++) {
     93			struct elem init = {};
     94
     95			/* lru_key cannot be used as loop induction variable
     96			 * otherwise the loop will be unbounded.
     97			 */
     98			lru_key = i;
     99
    100			/* add more elements into lru map to push out current
    101			 * element and force deletion of this timer
    102			 */
    103			bpf_map_update_elem(map, &lru_key, &init, 0);
    104			/* look it up to bump it into active list */
    105			bpf_map_lookup_elem(map, &lru_key);
    106
    107			/* keep adding until *key changes underneath,
    108			 * which means that key/timer memory was reused
    109			 */
    110			if (*key != LRU)
    111				break;
    112		}
    113
    114		/* check that the timer was removed */
    115		if (bpf_timer_cancel(timer) != -EINVAL)
    116			err |= 4;
    117		ok |= 1;
    118	}
    119	return 0;
    120}
    121
    122SEC("fentry/bpf_fentry_test1")
    123int BPF_PROG(test1, int a)
    124{
    125	struct bpf_timer *arr_timer, *lru_timer;
    126	struct elem init = {};
    127	int lru_key = LRU;
    128	int array_key = ARRAY;
    129
    130	arr_timer = bpf_map_lookup_elem(&array, &array_key);
    131	if (!arr_timer)
    132		return 0;
    133	bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC);
    134
    135	bpf_map_update_elem(&lru, &lru_key, &init, 0);
    136	lru_timer = bpf_map_lookup_elem(&lru, &lru_key);
    137	if (!lru_timer)
    138		return 0;
    139	bpf_timer_init(lru_timer, &lru, CLOCK_MONOTONIC);
    140
    141	bpf_timer_set_callback(arr_timer, timer_cb1);
    142	bpf_timer_start(arr_timer, 0 /* call timer_cb1 asap */, 0);
    143
    144	/* init more timers to check that array destruction
    145	 * doesn't leak timer memory.
    146	 */
    147	array_key = 0;
    148	arr_timer = bpf_map_lookup_elem(&array, &array_key);
    149	if (!arr_timer)
    150		return 0;
    151	bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC);
    152	return 0;
    153}
    154
    155/* callback for prealloc and non-prealloca hashtab timers */
    156static int timer_cb2(void *map, int *key, struct hmap_elem *val)
    157{
    158	if (*key == HTAB)
    159		callback_check--;
    160	else
    161		callback2_check--;
    162	if (val->counter > 0 && --val->counter) {
    163		/* re-arm the timer again to execute after 1 usec */
    164		bpf_timer_start(&val->timer, 1000, 0);
    165	} else if (*key == HTAB) {
    166		struct bpf_timer *arr_timer;
    167		int array_key = ARRAY;
    168
    169		/* cancel arr_timer otherwise bpf_fentry_test1 prog
    170		 * will stay alive forever.
    171		 */
    172		arr_timer = bpf_map_lookup_elem(&array, &array_key);
    173		if (!arr_timer)
    174			return 0;
    175		if (bpf_timer_cancel(arr_timer) != 1)
    176			/* bpf_timer_cancel should return 1 to indicate
    177			 * that arr_timer was active at this time
    178			 */
    179			err |= 8;
    180
    181		/* try to cancel ourself. It shouldn't deadlock. */
    182		if (bpf_timer_cancel(&val->timer) != -EDEADLK)
    183			err |= 16;
    184
    185		/* delete this key and this timer anyway.
    186		 * It shouldn't deadlock either.
    187		 */
    188		bpf_map_delete_elem(map, key);
    189
    190		/* in preallocated hashmap both 'key' and 'val' could have been
    191		 * reused to store another map element (like in LRU above),
    192		 * but in controlled test environment the below test works.
    193		 * It's not a use-after-free. The memory is owned by the map.
    194		 */
    195		if (bpf_timer_start(&val->timer, 1000, 0) != -EINVAL)
    196			err |= 32;
    197		ok |= 2;
    198	} else {
    199		if (*key != HTAB_MALLOC)
    200			err |= 64;
    201
    202		/* try to cancel ourself. It shouldn't deadlock. */
    203		if (bpf_timer_cancel(&val->timer) != -EDEADLK)
    204			err |= 128;
    205
    206		/* delete this key and this timer anyway.
    207		 * It shouldn't deadlock either.
    208		 */
    209		bpf_map_delete_elem(map, key);
    210
    211		/* in non-preallocated hashmap both 'key' and 'val' are RCU
    212		 * protected and still valid though this element was deleted
    213		 * from the map. Arm this timer for ~35 seconds. When callback
    214		 * finishes the call_rcu will invoke:
    215		 * htab_elem_free_rcu
    216		 *   check_and_free_timer
    217		 *     bpf_timer_cancel_and_free
    218		 * to cancel this 35 second sleep and delete the timer for real.
    219		 */
    220		if (bpf_timer_start(&val->timer, 1ull << 35, 0) != 0)
    221			err |= 256;
    222		ok |= 4;
    223	}
    224	return 0;
    225}
    226
    227int bpf_timer_test(void)
    228{
    229	struct hmap_elem *val;
    230	int key = HTAB, key_malloc = HTAB_MALLOC;
    231
    232	val = bpf_map_lookup_elem(&hmap, &key);
    233	if (val) {
    234		if (bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME) != 0)
    235			err |= 512;
    236		bpf_timer_set_callback(&val->timer, timer_cb2);
    237		bpf_timer_start(&val->timer, 1000, 0);
    238	}
    239	val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
    240	if (val) {
    241		if (bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME) != 0)
    242			err |= 1024;
    243		bpf_timer_set_callback(&val->timer, timer_cb2);
    244		bpf_timer_start(&val->timer, 1000, 0);
    245	}
    246	return 0;
    247}
    248
    249SEC("fentry/bpf_fentry_test2")
    250int BPF_PROG(test2, int a, int b)
    251{
    252	struct hmap_elem init = {}, *val;
    253	int key = HTAB, key_malloc = HTAB_MALLOC;
    254
    255	init.counter = 10; /* number of times to trigger timer_cb2 */
    256	bpf_map_update_elem(&hmap, &key, &init, 0);
    257	val = bpf_map_lookup_elem(&hmap, &key);
    258	if (val)
    259		bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
    260	/* update the same key to free the timer */
    261	bpf_map_update_elem(&hmap, &key, &init, 0);
    262
    263	bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
    264	val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
    265	if (val)
    266		bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
    267	/* update the same key to free the timer */
    268	bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
    269
    270	/* init more timers to check that htab operations
    271	 * don't leak timer memory.
    272	 */
    273	key = 0;
    274	bpf_map_update_elem(&hmap, &key, &init, 0);
    275	val = bpf_map_lookup_elem(&hmap, &key);
    276	if (val)
    277		bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
    278	bpf_map_delete_elem(&hmap, &key);
    279	bpf_map_update_elem(&hmap, &key, &init, 0);
    280	val = bpf_map_lookup_elem(&hmap, &key);
    281	if (val)
    282		bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
    283
    284	/* and with non-prealloc htab */
    285	key_malloc = 0;
    286	bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
    287	val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
    288	if (val)
    289		bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
    290	bpf_map_delete_elem(&hmap_malloc, &key_malloc);
    291	bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
    292	val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
    293	if (val)
    294		bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
    295
    296	return bpf_timer_test();
    297}