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 493540f7c6SAlexei Starovoitov __u64 bss_data; 503540f7c6SAlexei Starovoitov __u64 err; 513540f7c6SAlexei Starovoitov __u64 ok; 523540f7c6SAlexei Starovoitov __u64 callback_check = 52; 533540f7c6SAlexei Starovoitov __u64 callback2_check = 52; 543540f7c6SAlexei Starovoitov 553540f7c6SAlexei Starovoitov #define ARRAY 1 563540f7c6SAlexei Starovoitov #define HTAB 2 573540f7c6SAlexei Starovoitov #define HTAB_MALLOC 3 583540f7c6SAlexei Starovoitov #define LRU 4 593540f7c6SAlexei Starovoitov 603540f7c6SAlexei Starovoitov /* callback for array and lru timers */ 613540f7c6SAlexei Starovoitov static int timer_cb1(void *map, int *key, struct bpf_timer *timer) 623540f7c6SAlexei Starovoitov { 633540f7c6SAlexei Starovoitov /* increment bss variable twice. 643540f7c6SAlexei Starovoitov * Once via array timer callback and once via lru timer callback 653540f7c6SAlexei Starovoitov */ 663540f7c6SAlexei Starovoitov bss_data += 5; 673540f7c6SAlexei Starovoitov 683540f7c6SAlexei Starovoitov /* *key == 0 - the callback was called for array timer. 693540f7c6SAlexei Starovoitov * *key == 4 - the callback was called from lru timer. 703540f7c6SAlexei Starovoitov */ 713540f7c6SAlexei Starovoitov if (*key == ARRAY) { 723540f7c6SAlexei Starovoitov struct bpf_timer *lru_timer; 733540f7c6SAlexei Starovoitov int lru_key = LRU; 743540f7c6SAlexei Starovoitov 753540f7c6SAlexei Starovoitov /* rearm array timer to be called again in ~35 seconds */ 763540f7c6SAlexei Starovoitov if (bpf_timer_start(timer, 1ull << 35, 0) != 0) 773540f7c6SAlexei Starovoitov err |= 1; 783540f7c6SAlexei Starovoitov 793540f7c6SAlexei Starovoitov lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 803540f7c6SAlexei Starovoitov if (!lru_timer) 813540f7c6SAlexei Starovoitov return 0; 823540f7c6SAlexei Starovoitov bpf_timer_set_callback(lru_timer, timer_cb1); 833540f7c6SAlexei Starovoitov if (bpf_timer_start(lru_timer, 0, 0) != 0) 843540f7c6SAlexei Starovoitov err |= 2; 853540f7c6SAlexei Starovoitov } else if (*key == LRU) { 863540f7c6SAlexei Starovoitov int lru_key, i; 873540f7c6SAlexei Starovoitov 883540f7c6SAlexei Starovoitov for (i = LRU + 1; 893540f7c6SAlexei Starovoitov i <= 100 /* for current LRU eviction algorithm this number 903540f7c6SAlexei Starovoitov * should be larger than ~ lru->max_entries * 2 913540f7c6SAlexei Starovoitov */; 923540f7c6SAlexei Starovoitov i++) { 933540f7c6SAlexei Starovoitov struct elem init = {}; 943540f7c6SAlexei Starovoitov 953540f7c6SAlexei Starovoitov /* lru_key cannot be used as loop induction variable 963540f7c6SAlexei Starovoitov * otherwise the loop will be unbounded. 973540f7c6SAlexei Starovoitov */ 983540f7c6SAlexei Starovoitov lru_key = i; 993540f7c6SAlexei Starovoitov 1003540f7c6SAlexei Starovoitov /* add more elements into lru map to push out current 1013540f7c6SAlexei Starovoitov * element and force deletion of this timer 1023540f7c6SAlexei Starovoitov */ 1033540f7c6SAlexei Starovoitov bpf_map_update_elem(map, &lru_key, &init, 0); 1043540f7c6SAlexei Starovoitov /* look it up to bump it into active list */ 1053540f7c6SAlexei Starovoitov bpf_map_lookup_elem(map, &lru_key); 1063540f7c6SAlexei Starovoitov 1073540f7c6SAlexei Starovoitov /* keep adding until *key changes underneath, 1083540f7c6SAlexei Starovoitov * which means that key/timer memory was reused 1093540f7c6SAlexei Starovoitov */ 1103540f7c6SAlexei Starovoitov if (*key != LRU) 1113540f7c6SAlexei Starovoitov break; 1123540f7c6SAlexei Starovoitov } 1133540f7c6SAlexei Starovoitov 1143540f7c6SAlexei Starovoitov /* check that the timer was removed */ 1153540f7c6SAlexei Starovoitov if (bpf_timer_cancel(timer) != -EINVAL) 1163540f7c6SAlexei Starovoitov err |= 4; 1173540f7c6SAlexei Starovoitov ok |= 1; 1183540f7c6SAlexei Starovoitov } 1193540f7c6SAlexei Starovoitov return 0; 1203540f7c6SAlexei Starovoitov } 1213540f7c6SAlexei Starovoitov 1223540f7c6SAlexei Starovoitov SEC("fentry/bpf_fentry_test1") 123*a7c2ca3aSYonghong Song int BPF_PROG2(test1, int, a) 1243540f7c6SAlexei Starovoitov { 1253540f7c6SAlexei Starovoitov struct bpf_timer *arr_timer, *lru_timer; 1263540f7c6SAlexei Starovoitov struct elem init = {}; 1273540f7c6SAlexei Starovoitov int lru_key = LRU; 1283540f7c6SAlexei Starovoitov int array_key = ARRAY; 1293540f7c6SAlexei Starovoitov 1303540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1313540f7c6SAlexei Starovoitov if (!arr_timer) 1323540f7c6SAlexei Starovoitov return 0; 1333540f7c6SAlexei Starovoitov bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 1343540f7c6SAlexei Starovoitov 1353540f7c6SAlexei Starovoitov bpf_map_update_elem(&lru, &lru_key, &init, 0); 1363540f7c6SAlexei Starovoitov lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 1373540f7c6SAlexei Starovoitov if (!lru_timer) 1383540f7c6SAlexei Starovoitov return 0; 1393540f7c6SAlexei Starovoitov bpf_timer_init(lru_timer, &lru, CLOCK_MONOTONIC); 1403540f7c6SAlexei Starovoitov 1413540f7c6SAlexei Starovoitov bpf_timer_set_callback(arr_timer, timer_cb1); 1423540f7c6SAlexei Starovoitov bpf_timer_start(arr_timer, 0 /* call timer_cb1 asap */, 0); 1433540f7c6SAlexei Starovoitov 1443540f7c6SAlexei Starovoitov /* init more timers to check that array destruction 1453540f7c6SAlexei Starovoitov * doesn't leak timer memory. 1463540f7c6SAlexei Starovoitov */ 1473540f7c6SAlexei Starovoitov array_key = 0; 1483540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1493540f7c6SAlexei Starovoitov if (!arr_timer) 1503540f7c6SAlexei Starovoitov return 0; 1513540f7c6SAlexei Starovoitov bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 1523540f7c6SAlexei Starovoitov return 0; 1533540f7c6SAlexei Starovoitov } 1543540f7c6SAlexei Starovoitov 1553540f7c6SAlexei Starovoitov /* callback for prealloc and non-prealloca hashtab timers */ 1563540f7c6SAlexei Starovoitov static int timer_cb2(void *map, int *key, struct hmap_elem *val) 1573540f7c6SAlexei Starovoitov { 1583540f7c6SAlexei Starovoitov if (*key == HTAB) 1593540f7c6SAlexei Starovoitov callback_check--; 1603540f7c6SAlexei Starovoitov else 1613540f7c6SAlexei Starovoitov callback2_check--; 1623540f7c6SAlexei Starovoitov if (val->counter > 0 && --val->counter) { 1633540f7c6SAlexei Starovoitov /* re-arm the timer again to execute after 1 usec */ 1643540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 1653540f7c6SAlexei Starovoitov } else if (*key == HTAB) { 1663540f7c6SAlexei Starovoitov struct bpf_timer *arr_timer; 1673540f7c6SAlexei Starovoitov int array_key = ARRAY; 1683540f7c6SAlexei Starovoitov 1693540f7c6SAlexei Starovoitov /* cancel arr_timer otherwise bpf_fentry_test1 prog 1703540f7c6SAlexei Starovoitov * will stay alive forever. 1713540f7c6SAlexei Starovoitov */ 1723540f7c6SAlexei Starovoitov arr_timer = bpf_map_lookup_elem(&array, &array_key); 1733540f7c6SAlexei Starovoitov if (!arr_timer) 1743540f7c6SAlexei Starovoitov return 0; 1753540f7c6SAlexei Starovoitov if (bpf_timer_cancel(arr_timer) != 1) 1763540f7c6SAlexei Starovoitov /* bpf_timer_cancel should return 1 to indicate 1773540f7c6SAlexei Starovoitov * that arr_timer was active at this time 1783540f7c6SAlexei Starovoitov */ 1793540f7c6SAlexei Starovoitov err |= 8; 1803540f7c6SAlexei Starovoitov 1813540f7c6SAlexei Starovoitov /* try to cancel ourself. It shouldn't deadlock. */ 1823540f7c6SAlexei Starovoitov if (bpf_timer_cancel(&val->timer) != -EDEADLK) 1833540f7c6SAlexei Starovoitov err |= 16; 1843540f7c6SAlexei Starovoitov 1853540f7c6SAlexei Starovoitov /* delete this key and this timer anyway. 1863540f7c6SAlexei Starovoitov * It shouldn't deadlock either. 1873540f7c6SAlexei Starovoitov */ 1883540f7c6SAlexei Starovoitov bpf_map_delete_elem(map, key); 1893540f7c6SAlexei Starovoitov 1903540f7c6SAlexei Starovoitov /* in preallocated hashmap both 'key' and 'val' could have been 1913540f7c6SAlexei Starovoitov * reused to store another map element (like in LRU above), 1923540f7c6SAlexei Starovoitov * but in controlled test environment the below test works. 1933540f7c6SAlexei Starovoitov * It's not a use-after-free. The memory is owned by the map. 1943540f7c6SAlexei Starovoitov */ 1953540f7c6SAlexei Starovoitov if (bpf_timer_start(&val->timer, 1000, 0) != -EINVAL) 1963540f7c6SAlexei Starovoitov err |= 32; 1973540f7c6SAlexei Starovoitov ok |= 2; 1983540f7c6SAlexei Starovoitov } else { 1993540f7c6SAlexei Starovoitov if (*key != HTAB_MALLOC) 2003540f7c6SAlexei Starovoitov err |= 64; 2013540f7c6SAlexei Starovoitov 2023540f7c6SAlexei Starovoitov /* try to cancel ourself. It shouldn't deadlock. */ 2033540f7c6SAlexei Starovoitov if (bpf_timer_cancel(&val->timer) != -EDEADLK) 2043540f7c6SAlexei Starovoitov err |= 128; 2053540f7c6SAlexei Starovoitov 2063540f7c6SAlexei Starovoitov /* delete this key and this timer anyway. 2073540f7c6SAlexei Starovoitov * It shouldn't deadlock either. 2083540f7c6SAlexei Starovoitov */ 2093540f7c6SAlexei Starovoitov bpf_map_delete_elem(map, key); 2103540f7c6SAlexei Starovoitov 2113540f7c6SAlexei Starovoitov ok |= 4; 2123540f7c6SAlexei Starovoitov } 2133540f7c6SAlexei Starovoitov return 0; 2143540f7c6SAlexei Starovoitov } 2153540f7c6SAlexei Starovoitov 2163540f7c6SAlexei Starovoitov int bpf_timer_test(void) 2173540f7c6SAlexei Starovoitov { 2183540f7c6SAlexei Starovoitov struct hmap_elem *val; 2193540f7c6SAlexei Starovoitov int key = HTAB, key_malloc = HTAB_MALLOC; 2203540f7c6SAlexei Starovoitov 2213540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2223540f7c6SAlexei Starovoitov if (val) { 2233540f7c6SAlexei Starovoitov if (bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME) != 0) 2243540f7c6SAlexei Starovoitov err |= 512; 2253540f7c6SAlexei Starovoitov bpf_timer_set_callback(&val->timer, timer_cb2); 2263540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 2273540f7c6SAlexei Starovoitov } 2283540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2293540f7c6SAlexei Starovoitov if (val) { 2303540f7c6SAlexei Starovoitov if (bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME) != 0) 2313540f7c6SAlexei Starovoitov err |= 1024; 2323540f7c6SAlexei Starovoitov bpf_timer_set_callback(&val->timer, timer_cb2); 2333540f7c6SAlexei Starovoitov bpf_timer_start(&val->timer, 1000, 0); 2343540f7c6SAlexei Starovoitov } 2353540f7c6SAlexei Starovoitov return 0; 2363540f7c6SAlexei Starovoitov } 2373540f7c6SAlexei Starovoitov 2383540f7c6SAlexei Starovoitov SEC("fentry/bpf_fentry_test2") 239*a7c2ca3aSYonghong Song int BPF_PROG2(test2, int, a, int, b) 2403540f7c6SAlexei Starovoitov { 2413540f7c6SAlexei Starovoitov struct hmap_elem init = {}, *val; 2423540f7c6SAlexei Starovoitov int key = HTAB, key_malloc = HTAB_MALLOC; 2433540f7c6SAlexei Starovoitov 2443540f7c6SAlexei Starovoitov init.counter = 10; /* number of times to trigger timer_cb2 */ 2453540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2463540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2473540f7c6SAlexei Starovoitov if (val) 2483540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2493540f7c6SAlexei Starovoitov /* update the same key to free the timer */ 2503540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2513540f7c6SAlexei Starovoitov 2523540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2533540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2543540f7c6SAlexei Starovoitov if (val) 2553540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2563540f7c6SAlexei Starovoitov /* update the same key to free the timer */ 2573540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2583540f7c6SAlexei Starovoitov 2593540f7c6SAlexei Starovoitov /* init more timers to check that htab operations 2603540f7c6SAlexei Starovoitov * don't leak timer memory. 2613540f7c6SAlexei Starovoitov */ 2623540f7c6SAlexei Starovoitov key = 0; 2633540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2643540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2653540f7c6SAlexei Starovoitov if (val) 2663540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2673540f7c6SAlexei Starovoitov bpf_map_delete_elem(&hmap, &key); 2683540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap, &key, &init, 0); 2693540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap, &key); 2703540f7c6SAlexei Starovoitov if (val) 2713540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 2723540f7c6SAlexei Starovoitov 2733540f7c6SAlexei Starovoitov /* and with non-prealloc htab */ 2743540f7c6SAlexei Starovoitov key_malloc = 0; 2753540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2763540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2773540f7c6SAlexei Starovoitov if (val) 2783540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2793540f7c6SAlexei Starovoitov bpf_map_delete_elem(&hmap_malloc, &key_malloc); 2803540f7c6SAlexei Starovoitov bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 2813540f7c6SAlexei Starovoitov val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 2823540f7c6SAlexei Starovoitov if (val) 2833540f7c6SAlexei Starovoitov bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 2843540f7c6SAlexei Starovoitov 2853540f7c6SAlexei Starovoitov return bpf_timer_test(); 2863540f7c6SAlexei Starovoitov } 287