13540f7c6SAlexei Starovoitov // SPDX-License-Identifier: GPL-2.0 23540f7c6SAlexei Starovoitov /* Copyright (c) 2021 Facebook */ 33540f7c6SAlexei Starovoitov #include <linux/bpf.h> 43540f7c6SAlexei Starovoitov #include <time.h> 53540f7c6SAlexei Starovoitov #include <errno.h> 63540f7c6SAlexei Starovoitov #include <bpf/bpf_helpers.h> 73540f7c6SAlexei Starovoitov #include "bpf_tcp_helpers.h" 83540f7c6SAlexei Starovoitov 93540f7c6SAlexei Starovoitov char _license[] SEC("license") = "GPL"; 103540f7c6SAlexei Starovoitov struct hmap_elem { 113540f7c6SAlexei Starovoitov int counter; 123540f7c6SAlexei Starovoitov struct bpf_timer timer; 133540f7c6SAlexei Starovoitov struct bpf_spin_lock lock; /* unused */ 143540f7c6SAlexei Starovoitov }; 153540f7c6SAlexei Starovoitov 163540f7c6SAlexei Starovoitov struct { 173540f7c6SAlexei Starovoitov __uint(type, BPF_MAP_TYPE_HASH); 183540f7c6SAlexei Starovoitov __uint(max_entries, 1000); 193540f7c6SAlexei Starovoitov __type(key, int); 203540f7c6SAlexei Starovoitov __type(value, struct hmap_elem); 213540f7c6SAlexei Starovoitov } hmap SEC(".maps"); 223540f7c6SAlexei Starovoitov 233540f7c6SAlexei Starovoitov struct { 243540f7c6SAlexei Starovoitov __uint(type, BPF_MAP_TYPE_HASH); 253540f7c6SAlexei Starovoitov __uint(map_flags, BPF_F_NO_PREALLOC); 263540f7c6SAlexei Starovoitov __uint(max_entries, 1000); 273540f7c6SAlexei Starovoitov __type(key, int); 283540f7c6SAlexei Starovoitov __type(value, struct hmap_elem); 293540f7c6SAlexei Starovoitov } hmap_malloc SEC(".maps"); 303540f7c6SAlexei Starovoitov 313540f7c6SAlexei Starovoitov struct elem { 323540f7c6SAlexei Starovoitov struct bpf_timer t; 333540f7c6SAlexei Starovoitov }; 343540f7c6SAlexei Starovoitov 353540f7c6SAlexei Starovoitov struct { 363540f7c6SAlexei Starovoitov __uint(type, BPF_MAP_TYPE_ARRAY); 373540f7c6SAlexei Starovoitov __uint(max_entries, 2); 383540f7c6SAlexei Starovoitov __type(key, int); 393540f7c6SAlexei Starovoitov __type(value, struct elem); 403540f7c6SAlexei Starovoitov } array SEC(".maps"); 413540f7c6SAlexei Starovoitov 423540f7c6SAlexei Starovoitov struct { 433540f7c6SAlexei Starovoitov __uint(type, BPF_MAP_TYPE_LRU_HASH); 443540f7c6SAlexei Starovoitov __uint(max_entries, 4); 453540f7c6SAlexei Starovoitov __type(key, int); 463540f7c6SAlexei Starovoitov __type(value, struct elem); 473540f7c6SAlexei Starovoitov } lru SEC(".maps"); 483540f7c6SAlexei Starovoitov 49*944459e8STero Kristo struct { 50*944459e8STero Kristo __uint(type, BPF_MAP_TYPE_ARRAY); 51*944459e8STero Kristo __uint(max_entries, 1); 52*944459e8STero Kristo __type(key, int); 53*944459e8STero Kristo __type(value, struct elem); 54*944459e8STero Kristo } abs_timer SEC(".maps"); 55*944459e8STero Kristo 563540f7c6SAlexei Starovoitov __u64 bss_data; 57*944459e8STero Kristo __u64 abs_data; 583540f7c6SAlexei Starovoitov __u64 err; 593540f7c6SAlexei Starovoitov __u64 ok; 603540f7c6SAlexei Starovoitov __u64 callback_check = 52; 613540f7c6SAlexei Starovoitov __u64 callback2_check = 52; 623540f7c6SAlexei Starovoitov 633540f7c6SAlexei Starovoitov #define ARRAY 1 643540f7c6SAlexei Starovoitov #define HTAB 2 653540f7c6SAlexei Starovoitov #define HTAB_MALLOC 3 663540f7c6SAlexei Starovoitov #define LRU 4 673540f7c6SAlexei Starovoitov 683540f7c6SAlexei Starovoitov /* callback for array and lru timers */ 693540f7c6SAlexei Starovoitov static int timer_cb1(void *map, int *key, struct bpf_timer *timer) 703540f7c6SAlexei Starovoitov { 713540f7c6SAlexei Starovoitov /* increment bss variable twice. 723540f7c6SAlexei Starovoitov * Once via array timer callback and once via lru timer callback 733540f7c6SAlexei Starovoitov */ 743540f7c6SAlexei Starovoitov bss_data += 5; 753540f7c6SAlexei Starovoitov 763540f7c6SAlexei Starovoitov /* *key == 0 - the callback was called for array timer. 773540f7c6SAlexei Starovoitov * *key == 4 - the callback was called from lru timer. 783540f7c6SAlexei Starovoitov */ 793540f7c6SAlexei Starovoitov if (*key == ARRAY) { 803540f7c6SAlexei Starovoitov struct bpf_timer *lru_timer; 813540f7c6SAlexei Starovoitov int lru_key = LRU; 823540f7c6SAlexei Starovoitov 833540f7c6SAlexei Starovoitov /* rearm array timer to be called again in ~35 seconds */ 843540f7c6SAlexei Starovoitov if (bpf_timer_start(timer, 1ull << 35, 0) != 0) 853540f7c6SAlexei Starovoitov err |= 1; 863540f7c6SAlexei Starovoitov 873540f7c6SAlexei Starovoitov lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 883540f7c6SAlexei Starovoitov if (!lru_timer) 893540f7c6SAlexei Starovoitov return 0; 903540f7c6SAlexei Starovoitov bpf_timer_set_callback(lru_timer, timer_cb1); 913540f7c6SAlexei Starovoitov if (bpf_timer_start(lru_timer, 0, 0) != 0) 923540f7c6SAlexei Starovoitov err |= 2; 933540f7c6SAlexei Starovoitov } else if (*key == LRU) { 943540f7c6SAlexei Starovoitov int lru_key, i; 953540f7c6SAlexei Starovoitov 963540f7c6SAlexei Starovoitov for (i = LRU + 1; 973540f7c6SAlexei Starovoitov i <= 100 /* for current LRU eviction algorithm this number 983540f7c6SAlexei Starovoitov * should be larger than ~ lru->max_entries * 2 993540f7c6SAlexei Starovoitov */; 1003540f7c6SAlexei Starovoitov i++) { 1013540f7c6SAlexei Starovoitov struct elem init = {}; 1023540f7c6SAlexei Starovoitov 1033540f7c6SAlexei Starovoitov /* lru_key cannot be used as loop induction variable 1043540f7c6SAlexei Starovoitov * otherwise the loop will be unbounded. 1053540f7c6SAlexei Starovoitov */ 1063540f7c6SAlexei Starovoitov lru_key = i; 1073540f7c6SAlexei Starovoitov 1083540f7c6SAlexei Starovoitov /* add more elements into lru map to push out current 1093540f7c6SAlexei Starovoitov * element and force deletion of this timer 1103540f7c6SAlexei Starovoitov */ 1113540f7c6SAlexei Starovoitov bpf_map_update_elem(map, &lru_key, &init, 0); 1123540f7c6SAlexei Starovoitov /* look it up to bump it into active list */ 1133540f7c6SAlexei Starovoitov bpf_map_lookup_elem(map, &lru_key); 1143540f7c6SAlexei Starovoitov 1153540f7c6SAlexei Starovoitov /* keep adding until *key changes underneath, 1163540f7c6SAlexei Starovoitov * which means that key/timer memory was reused 1173540f7c6SAlexei Starovoitov */ 1183540f7c6SAlexei Starovoitov if (*key != LRU) 1193540f7c6SAlexei Starovoitov break; 1203540f7c6SAlexei Starovoitov } 1213540f7c6SAlexei Starovoitov 1223540f7c6SAlexei Starovoitov /* check that the timer was removed */ 1233540f7c6SAlexei Starovoitov if (bpf_timer_cancel(timer) != -EINVAL) 1243540f7c6SAlexei Starovoitov err |= 4; 1253540f7c6SAlexei Starovoitov ok |= 1; 1263540f7c6SAlexei Starovoitov } 1273540f7c6SAlexei Starovoitov return 0; 1283540f7c6SAlexei Starovoitov } 1293540f7c6SAlexei Starovoitov 1303540f7c6SAlexei Starovoitov SEC("fentry/bpf_fentry_test1") 131a7c2ca3aSYonghong Song int BPF_PROG2(test1, int, a) 1323540f7c6SAlexei Starovoitov { 1333540f7c6SAlexei Starovoitov struct bpf_timer *arr_timer, *lru_timer; 1343540f7c6SAlexei Starovoitov struct elem init = {}; 1353540f7c6SAlexei Starovoitov int lru_key = LRU; 1363540f7c6SAlexei Starovoitov int array_key = ARRAY; 1373540f7c6SAlexei Starovoitov 1383540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1393540f7c6SAlexei Starovoitov if (!arr_timer) 1403540f7c6SAlexei Starovoitov return 0; 1413540f7c6SAlexei Starovoitov bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 1423540f7c6SAlexei Starovoitov 1433540f7c6SAlexei Starovoitov bpf_map_update_elem(&lru, &lru_key, &init, 0); 1443540f7c6SAlexei Starovoitov lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 1453540f7c6SAlexei Starovoitov if (!lru_timer) 1463540f7c6SAlexei Starovoitov return 0; 1473540f7c6SAlexei Starovoitov bpf_timer_init(lru_timer, &lru, CLOCK_MONOTONIC); 1483540f7c6SAlexei Starovoitov 1493540f7c6SAlexei Starovoitov bpf_timer_set_callback(arr_timer, timer_cb1); 1503540f7c6SAlexei Starovoitov bpf_timer_start(arr_timer, 0 /* call timer_cb1 asap */, 0); 1513540f7c6SAlexei Starovoitov 1523540f7c6SAlexei Starovoitov /* init more timers to check that array destruction 1533540f7c6SAlexei Starovoitov * doesn't leak timer memory. 1543540f7c6SAlexei Starovoitov */ 1553540f7c6SAlexei Starovoitov array_key = 0; 1563540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1573540f7c6SAlexei Starovoitov if (!arr_timer) 1583540f7c6SAlexei Starovoitov return 0; 1593540f7c6SAlexei Starovoitov bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 1603540f7c6SAlexei Starovoitov return 0; 1613540f7c6SAlexei Starovoitov } 1623540f7c6SAlexei Starovoitov 1633540f7c6SAlexei Starovoitov /* callback for prealloc and non-prealloca hashtab timers */ 1643540f7c6SAlexei Starovoitov static int timer_cb2(void *map, int *key, struct hmap_elem *val) 1653540f7c6SAlexei Starovoitov { 1663540f7c6SAlexei Starovoitov if (*key == HTAB) 1673540f7c6SAlexei Starovoitov callback_check--; 1683540f7c6SAlexei Starovoitov else 1693540f7c6SAlexei Starovoitov callback2_check--; 1703540f7c6SAlexei Starovoitov if (val->counter > 0 && --val->counter) { 1713540f7c6SAlexei Starovoitov /* re-arm the timer again to execute after 1 usec */ 1723540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 1733540f7c6SAlexei Starovoitov } else if (*key == HTAB) { 1743540f7c6SAlexei Starovoitov struct bpf_timer *arr_timer; 1753540f7c6SAlexei Starovoitov int array_key = ARRAY; 1763540f7c6SAlexei Starovoitov 1773540f7c6SAlexei Starovoitov /* cancel arr_timer otherwise bpf_fentry_test1 prog 1783540f7c6SAlexei Starovoitov * will stay alive forever. 1793540f7c6SAlexei Starovoitov */ 1803540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1813540f7c6SAlexei Starovoitov if (!arr_timer) 1823540f7c6SAlexei Starovoitov return 0; 1833540f7c6SAlexei Starovoitov if (bpf_timer_cancel(arr_timer) != 1) 1843540f7c6SAlexei Starovoitov /* bpf_timer_cancel should return 1 to indicate 1853540f7c6SAlexei Starovoitov * that arr_timer was active at this time 1863540f7c6SAlexei Starovoitov */ 1873540f7c6SAlexei Starovoitov err |= 8; 1883540f7c6SAlexei Starovoitov 1893540f7c6SAlexei Starovoitov /* try to cancel ourself. It shouldn't deadlock. */ 1903540f7c6SAlexei Starovoitov if (bpf_timer_cancel(&val->timer) != -EDEADLK) 1913540f7c6SAlexei Starovoitov err |= 16; 1923540f7c6SAlexei Starovoitov 1933540f7c6SAlexei Starovoitov /* delete this key and this timer anyway. 1943540f7c6SAlexei Starovoitov * It shouldn't deadlock either. 1953540f7c6SAlexei Starovoitov */ 1963540f7c6SAlexei Starovoitov bpf_map_delete_elem(map, key); 1973540f7c6SAlexei Starovoitov 1983540f7c6SAlexei Starovoitov /* in preallocated hashmap both 'key' and 'val' could have been 1993540f7c6SAlexei Starovoitov * reused to store another map element (like in LRU above), 2003540f7c6SAlexei Starovoitov * but in controlled test environment the below test works. 2013540f7c6SAlexei Starovoitov * It's not a use-after-free. The memory is owned by the map. 2023540f7c6SAlexei Starovoitov */ 2033540f7c6SAlexei Starovoitov if (bpf_timer_start(&val->timer, 1000, 0) != -EINVAL) 2043540f7c6SAlexei Starovoitov err |= 32; 2053540f7c6SAlexei Starovoitov ok |= 2; 2063540f7c6SAlexei Starovoitov } else { 2073540f7c6SAlexei Starovoitov if (*key != HTAB_MALLOC) 2083540f7c6SAlexei Starovoitov err |= 64; 2093540f7c6SAlexei Starovoitov 2103540f7c6SAlexei Starovoitov /* try to cancel ourself. It shouldn't deadlock. */ 2113540f7c6SAlexei Starovoitov if (bpf_timer_cancel(&val->timer) != -EDEADLK) 2123540f7c6SAlexei Starovoitov err |= 128; 2133540f7c6SAlexei Starovoitov 2143540f7c6SAlexei Starovoitov /* delete this key and this timer anyway. 2153540f7c6SAlexei Starovoitov * It shouldn't deadlock either. 2163540f7c6SAlexei Starovoitov */ 2173540f7c6SAlexei Starovoitov bpf_map_delete_elem(map, key); 2183540f7c6SAlexei Starovoitov 2193540f7c6SAlexei Starovoitov ok |= 4; 2203540f7c6SAlexei Starovoitov } 2213540f7c6SAlexei Starovoitov return 0; 2223540f7c6SAlexei Starovoitov } 2233540f7c6SAlexei Starovoitov 2243540f7c6SAlexei Starovoitov int bpf_timer_test(void) 2253540f7c6SAlexei Starovoitov { 2263540f7c6SAlexei Starovoitov struct hmap_elem *val; 2273540f7c6SAlexei Starovoitov int key = HTAB, key_malloc = HTAB_MALLOC; 2283540f7c6SAlexei Starovoitov 2293540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2303540f7c6SAlexei Starovoitov if (val) { 2313540f7c6SAlexei Starovoitov if (bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME) != 0) 2323540f7c6SAlexei Starovoitov err |= 512; 2333540f7c6SAlexei Starovoitov bpf_timer_set_callback(&val->timer, timer_cb2); 2343540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 2353540f7c6SAlexei Starovoitov } 2363540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2373540f7c6SAlexei Starovoitov if (val) { 2383540f7c6SAlexei Starovoitov if (bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME) != 0) 2393540f7c6SAlexei Starovoitov err |= 1024; 2403540f7c6SAlexei Starovoitov bpf_timer_set_callback(&val->timer, timer_cb2); 2413540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 2423540f7c6SAlexei Starovoitov } 2433540f7c6SAlexei Starovoitov return 0; 2443540f7c6SAlexei Starovoitov } 2453540f7c6SAlexei Starovoitov 2463540f7c6SAlexei Starovoitov SEC("fentry/bpf_fentry_test2") 247a7c2ca3aSYonghong Song int BPF_PROG2(test2, int, a, int, b) 2483540f7c6SAlexei Starovoitov { 2493540f7c6SAlexei Starovoitov struct hmap_elem init = {}, *val; 2503540f7c6SAlexei Starovoitov int key = HTAB, key_malloc = HTAB_MALLOC; 2513540f7c6SAlexei Starovoitov 2523540f7c6SAlexei Starovoitov init.counter = 10; /* number of times to trigger timer_cb2 */ 2533540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2543540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2553540f7c6SAlexei Starovoitov if (val) 2563540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2573540f7c6SAlexei Starovoitov /* update the same key to free the timer */ 2583540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2593540f7c6SAlexei Starovoitov 2603540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2613540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2623540f7c6SAlexei Starovoitov if (val) 2633540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2643540f7c6SAlexei Starovoitov /* update the same key to free the timer */ 2653540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2663540f7c6SAlexei Starovoitov 2673540f7c6SAlexei Starovoitov /* init more timers to check that htab operations 2683540f7c6SAlexei Starovoitov * don't leak timer memory. 2693540f7c6SAlexei Starovoitov */ 2703540f7c6SAlexei Starovoitov key = 0; 2713540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2723540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2733540f7c6SAlexei Starovoitov if (val) 2743540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2753540f7c6SAlexei Starovoitov bpf_map_delete_elem(&hmap, &key); 2763540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2773540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2783540f7c6SAlexei Starovoitov if (val) 2793540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2803540f7c6SAlexei Starovoitov 2813540f7c6SAlexei Starovoitov /* and with non-prealloc htab */ 2823540f7c6SAlexei Starovoitov key_malloc = 0; 2833540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2843540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2853540f7c6SAlexei Starovoitov if (val) 2863540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2873540f7c6SAlexei Starovoitov bpf_map_delete_elem(&hmap_malloc, &key_malloc); 2883540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2893540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2903540f7c6SAlexei Starovoitov if (val) 2913540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2923540f7c6SAlexei Starovoitov 2933540f7c6SAlexei Starovoitov return bpf_timer_test(); 2943540f7c6SAlexei Starovoitov } 295*944459e8STero Kristo 296*944459e8STero Kristo /* callback for absolute timer */ 297*944459e8STero Kristo static int timer_cb3(void *map, int *key, struct bpf_timer *timer) 298*944459e8STero Kristo { 299*944459e8STero Kristo abs_data += 6; 300*944459e8STero Kristo 301*944459e8STero Kristo if (abs_data < 12) { 302*944459e8STero Kristo bpf_timer_start(timer, bpf_ktime_get_boot_ns() + 1000, 303*944459e8STero Kristo BPF_F_TIMER_ABS); 304*944459e8STero Kristo } else { 305*944459e8STero Kristo /* Re-arm timer ~35 seconds in future */ 306*944459e8STero Kristo bpf_timer_start(timer, bpf_ktime_get_boot_ns() + (1ull << 35), 307*944459e8STero Kristo BPF_F_TIMER_ABS); 308*944459e8STero Kristo } 309*944459e8STero Kristo 310*944459e8STero Kristo return 0; 311*944459e8STero Kristo } 312*944459e8STero Kristo 313*944459e8STero Kristo SEC("fentry/bpf_fentry_test3") 314*944459e8STero Kristo int BPF_PROG2(test3, int, a) 315*944459e8STero Kristo { 316*944459e8STero Kristo int key = 0; 317*944459e8STero Kristo struct bpf_timer *timer; 318*944459e8STero Kristo 319*944459e8STero Kristo bpf_printk("test3"); 320*944459e8STero Kristo 321*944459e8STero Kristo timer = bpf_map_lookup_elem(&abs_timer, &key); 322*944459e8STero Kristo if (timer) { 323*944459e8STero Kristo if (bpf_timer_init(timer, &abs_timer, CLOCK_BOOTTIME) != 0) 324*944459e8STero Kristo err |= 2048; 325*944459e8STero Kristo bpf_timer_set_callback(timer, timer_cb3); 326*944459e8STero Kristo bpf_timer_start(timer, bpf_ktime_get_boot_ns() + 1000, 327*944459e8STero Kristo BPF_F_TIMER_ABS); 328*944459e8STero Kristo } 329*944459e8STero Kristo 330*944459e8STero Kristo return 0; 331*944459e8STero Kristo } 332