55
66#include "test_btf_map_in_map.skel.h"
77
8+ static int duration ;
9+
10+ static __u32 bpf_map_id (struct bpf_map * map )
11+ {
12+ struct bpf_map_info info ;
13+ __u32 info_len = sizeof (info );
14+ int err ;
15+
16+ memset (& info , 0 , info_len );
17+ err = bpf_obj_get_info_by_fd (bpf_map__fd (map ), & info , & info_len );
18+ if (err )
19+ return 0 ;
20+ return info .id ;
21+ }
22+
23+ /*
24+ * Trigger synchronize_rcu() in kernel.
25+ *
26+ * ARRAY_OF_MAPS/HASH_OF_MAPS lookup/update operations trigger synchronize_rcu()
27+ * if looking up an existing non-NULL element or updating the map with a valid
28+ * inner map FD. Use this fact to trigger synchronize_rcu(): create map-in-map,
29+ * create a trivial ARRAY map, update map-in-map with ARRAY inner map. Then
30+ * cleanup. At the end, at least one synchronize_rcu() would be called.
31+ */
32+ static int kern_sync_rcu (void )
33+ {
34+ int inner_map_fd , outer_map_fd , err , zero = 0 ;
35+
36+ inner_map_fd = bpf_create_map (BPF_MAP_TYPE_ARRAY , 4 , 4 , 1 , 0 );
37+ if (CHECK (inner_map_fd < 0 , "inner_map_create" , "failed %d\n" , - errno ))
38+ return -1 ;
39+
40+ outer_map_fd = bpf_create_map_in_map (BPF_MAP_TYPE_ARRAY_OF_MAPS , NULL ,
41+ sizeof (int ), inner_map_fd , 1 , 0 );
42+ if (CHECK (outer_map_fd < 0 , "outer_map_create" , "failed %d\n" , - errno )) {
43+ close (inner_map_fd );
44+ return -1 ;
45+ }
46+
47+ err = bpf_map_update_elem (outer_map_fd , & zero , & inner_map_fd , 0 );
48+ if (err )
49+ err = - errno ;
50+ CHECK (err , "outer_map_update" , "failed %d\n" , err );
51+ close (inner_map_fd );
52+ close (outer_map_fd );
53+ return err ;
54+ }
55+
856void test_btf_map_in_map (void )
957{
10- int duration = 0 , err , key = 0 , val ;
11- struct test_btf_map_in_map * skel ;
58+ int err , key = 0 , val , i ;
59+ struct test_btf_map_in_map * skel ;
60+ int outer_arr_fd , outer_hash_fd ;
61+ int fd , map1_fd , map2_fd , map1_id , map2_id ;
1262
1363 skel = test_btf_map_in_map__open_and_load ();
1464 if (CHECK (!skel , "skel_open" , "failed to open&load skeleton\n" ))
@@ -18,32 +68,78 @@ void test_btf_map_in_map(void)
1868 if (CHECK (err , "skel_attach" , "skeleton attach failed: %d\n" , err ))
1969 goto cleanup ;
2070
71+ map1_fd = bpf_map__fd (skel -> maps .inner_map1 );
72+ map2_fd = bpf_map__fd (skel -> maps .inner_map2 );
73+ outer_arr_fd = bpf_map__fd (skel -> maps .outer_arr );
74+ outer_hash_fd = bpf_map__fd (skel -> maps .outer_hash );
75+
2176 /* inner1 = input, inner2 = input + 1 */
22- val = bpf_map__fd (skel -> maps .inner_map1 );
23- bpf_map_update_elem (bpf_map__fd ( skel -> maps . outer_arr ) , & key , & val , 0 );
24- val = bpf_map__fd (skel -> maps .inner_map2 );
25- bpf_map_update_elem (bpf_map__fd ( skel -> maps . outer_hash ) , & key , & val , 0 );
77+ map1_fd = bpf_map__fd (skel -> maps .inner_map1 );
78+ bpf_map_update_elem (outer_arr_fd , & key , & map1_fd , 0 );
79+ map2_fd = bpf_map__fd (skel -> maps .inner_map2 );
80+ bpf_map_update_elem (outer_hash_fd , & key , & map2_fd , 0 );
2681 skel -> bss -> input = 1 ;
2782 usleep (1 );
2883
29- bpf_map_lookup_elem (bpf_map__fd ( skel -> maps . inner_map1 ) , & key , & val );
84+ bpf_map_lookup_elem (map1_fd , & key , & val );
3085 CHECK (val != 1 , "inner1" , "got %d != exp %d\n" , val , 1 );
31- bpf_map_lookup_elem (bpf_map__fd ( skel -> maps . inner_map2 ) , & key , & val );
86+ bpf_map_lookup_elem (map2_fd , & key , & val );
3287 CHECK (val != 2 , "inner2" , "got %d != exp %d\n" , val , 2 );
3388
3489 /* inner1 = input + 1, inner2 = input */
35- val = bpf_map__fd (skel -> maps .inner_map2 );
36- bpf_map_update_elem (bpf_map__fd (skel -> maps .outer_arr ), & key , & val , 0 );
37- val = bpf_map__fd (skel -> maps .inner_map1 );
38- bpf_map_update_elem (bpf_map__fd (skel -> maps .outer_hash ), & key , & val , 0 );
90+ bpf_map_update_elem (outer_arr_fd , & key , & map2_fd , 0 );
91+ bpf_map_update_elem (outer_hash_fd , & key , & map1_fd , 0 );
3992 skel -> bss -> input = 3 ;
4093 usleep (1 );
4194
42- bpf_map_lookup_elem (bpf_map__fd ( skel -> maps . inner_map1 ) , & key , & val );
95+ bpf_map_lookup_elem (map1_fd , & key , & val );
4396 CHECK (val != 4 , "inner1" , "got %d != exp %d\n" , val , 4 );
44- bpf_map_lookup_elem (bpf_map__fd ( skel -> maps . inner_map2 ) , & key , & val );
97+ bpf_map_lookup_elem (map2_fd , & key , & val );
4598 CHECK (val != 3 , "inner2" , "got %d != exp %d\n" , val , 3 );
4699
100+ for (i = 0 ; i < 5 ; i ++ ) {
101+ val = i % 2 ? map1_fd : map2_fd ;
102+ err = bpf_map_update_elem (outer_hash_fd , & key , & val , 0 );
103+ if (CHECK_FAIL (err )) {
104+ printf ("failed to update hash_of_maps on iter #%d\n" , i );
105+ goto cleanup ;
106+ }
107+ err = bpf_map_update_elem (outer_arr_fd , & key , & val , 0 );
108+ if (CHECK_FAIL (err )) {
109+ printf ("failed to update hash_of_maps on iter #%d\n" , i );
110+ goto cleanup ;
111+ }
112+ }
113+
114+ map1_id = bpf_map_id (skel -> maps .inner_map1 );
115+ map2_id = bpf_map_id (skel -> maps .inner_map2 );
116+ CHECK (map1_id == 0 , "map1_id" , "failed to get ID 1\n" );
117+ CHECK (map2_id == 0 , "map2_id" , "failed to get ID 2\n" );
118+
119+ test_btf_map_in_map__destroy (skel );
120+ skel = NULL ;
121+
122+ /* we need to either wait for or force synchronize_rcu(), before
123+ * checking for "still exists" condition, otherwise map could still be
124+ * resolvable by ID, causing false positives.
125+ *
126+ * Older kernels (5.8 and earlier) freed map only after two
127+ * synchronize_rcu()s, so trigger two, to be entirely sure.
128+ */
129+ CHECK (kern_sync_rcu (), "sync_rcu" , "failed\n" );
130+ CHECK (kern_sync_rcu (), "sync_rcu" , "failed\n" );
131+
132+ fd = bpf_map_get_fd_by_id (map1_id );
133+ if (CHECK (fd >= 0 , "map1_leak" , "inner_map1 leaked!\n" )) {
134+ close (fd );
135+ goto cleanup ;
136+ }
137+ fd = bpf_map_get_fd_by_id (map2_id );
138+ if (CHECK (fd >= 0 , "map2_leak" , "inner_map2 leaked!\n" )) {
139+ close (fd );
140+ goto cleanup ;
141+ }
142+
47143cleanup :
48144 test_btf_map_in_map__destroy (skel );
49145}
0 commit comments