1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless requied by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #define LOG_TAG "BpfTest"
19 
20 #include <arpa/inet.h>
21 #include <assert.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <linux/pfkeyv2.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 
30 #include <thread>
31 
32 #include <android-base/file.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/unique_fd.h>
35 #include <gtest/gtest.h>
36 #include <utils/Log.h>
37 
38 #include "bpf/BpfMap.h"
39 #include "bpf/BpfUtils.h"
40 #include "kern.h"
41 #include "libbpf_android.h"
42 
43 using android::base::unique_fd;
44 using namespace android::bpf;
45 
46 namespace android {
47 
TEST(BpfTest,bpfMapPinTest)48 TEST(BpfTest, bpfMapPinTest) {
49   SKIP_IF_BPF_NOT_SUPPORTED;
50 
51   EXPECT_EQ(0, setrlimitForTest());
52   const char* bpfMapPath = "/sys/fs/bpf/testMap";
53   int ret = access(bpfMapPath, F_OK);
54   if (!ret) {
55     ASSERT_EQ(0, remove(bpfMapPath));
56   } else {
57     ASSERT_EQ(errno, ENOENT);
58   }
59 
60   android::base::unique_fd mapfd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t),
61                                            sizeof(uint32_t), 10,
62                                            BPF_F_NO_PREALLOC));
63   ASSERT_LT(0, mapfd) << "create map failed with error: " << strerror(errno);
64   ASSERT_EQ(0, bpfFdPin(mapfd, bpfMapPath))
65       << "pin map failed with error: " << strerror(errno);
66   ASSERT_EQ(0, access(bpfMapPath, F_OK));
67   ASSERT_EQ(0, remove(bpfMapPath));
68 }
69 
70 #define BPF_SRC_PATH "/data/local/tmp"
71 
72 #if defined(__aarch64__) || defined(__x86_64__)
73 #define BPF_SRC_NAME "/64/kern.o"
74 #else
75 #define BPF_SRC_NAME "/32/kern.o"
76 #endif
77 
78 #define BPF_PATH "/sys/fs/bpf"
79 #define TEST_PROG_PATH BPF_PATH "/prog_kern_skfilter_test"
80 #define TEST_STATS_MAP_A_PATH BPF_PATH "/map_kern_test_stats_map_A"
81 #define TEST_STATS_MAP_B_PATH BPF_PATH "/map_kern_test_stats_map_B"
82 #define TEST_CONFIGURATION_MAP_PATH BPF_PATH "/map_kern_test_configuration_map"
83 
84 constexpr int ACTIVE_MAP_KEY = 1;
85 
86 class BpfRaceTest : public ::testing::Test {
87  protected:
BpfRaceTest()88   BpfRaceTest() {}
89   BpfMap<uint64_t, stats_value> cookieStatsMap[2];
90   BpfMap<uint32_t, uint32_t> configurationMap;
91   bool stop;
92   std::thread tds[NUM_SOCKETS];
93 
workerThread(int prog_fd,bool * stop)94   static void workerThread(int prog_fd, bool *stop) {
95     struct sockaddr_in6 remote = {.sin6_family = AF_INET6};
96     struct sockaddr_in6 local;
97     uint64_t j = 0;
98     int recvSock, sendSock, recv_len;
99     char buf[strlen("msg: 18446744073709551615")];
100     int res;
101     socklen_t slen = sizeof(remote);
102 
103     recvSock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
104     EXPECT_NE(-1, recvSock);
105     std::string address = android::base::StringPrintf("::1");
106     EXPECT_NE(0, inet_pton(AF_INET6, address.c_str(), &remote.sin6_addr));
107     EXPECT_NE(-1, bind(recvSock, (struct sockaddr *)&remote, sizeof(remote)));
108     EXPECT_EQ(0, getsockname(recvSock, (struct sockaddr *)&remote, &slen));
109     sendSock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
110     EXPECT_NE(-1, sendSock) << "send socket create failed!\n";
111     EXPECT_NE(-1, setsockopt(recvSock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
112                              sizeof(prog_fd)))
113         << "attach bpf program failed"
114         << android::base::StringPrintf("%s\n", strerror(errno));
115 
116     // Keep sending and receiving packet until test end.
117     while (!*stop) {
118       std::string id = android::base::StringPrintf("msg: %" PRIu64 "\n", j);
119       res = sendto(sendSock, &id, id.length(), 0, (struct sockaddr *)&remote,
120                    slen);
121       EXPECT_EQ(id.size(), res);
122       recv_len = recvfrom(recvSock, &buf, sizeof(buf), 0,
123                           (struct sockaddr *)&local, &slen);
124       EXPECT_EQ(id.size(), recv_len);
125     }
126   }
127 
SetUp()128   void SetUp() {
129     SKIP_IF_BPF_NOT_SUPPORTED;
130 
131     EXPECT_EQ(0, setrlimitForTest());
132     int ret = access(TEST_PROG_PATH, R_OK);
133     // Always create a new program and remove the pinned program after program
134     // loading is done.
135     if (ret == 0) {
136       remove(TEST_PROG_PATH);
137     }
138     std::string progSrcPath = BPF_SRC_PATH BPF_SRC_NAME;
139     // 0 != 2 means ENOENT - ie. missing bpf program.
140     ASSERT_EQ(0, access(progSrcPath.c_str(), R_OK) ? errno : 0);
141     bool critical = true;
142     ASSERT_EQ(0, android::bpf::loadProg(progSrcPath.c_str(), &critical));
143     ASSERT_EQ(false, critical);
144 
145     EXPECT_RESULT_OK(cookieStatsMap[0].init(TEST_STATS_MAP_A_PATH));
146     EXPECT_RESULT_OK(cookieStatsMap[1].init(TEST_STATS_MAP_B_PATH));
147     EXPECT_RESULT_OK(configurationMap.init(TEST_CONFIGURATION_MAP_PATH));
148     EXPECT_TRUE(cookieStatsMap[0].isValid());
149     EXPECT_TRUE(cookieStatsMap[1].isValid());
150     EXPECT_TRUE(configurationMap.isValid());
151     // Start several threads to send and receive packets with an eBPF program
152     // attached to the socket.
153     stop = false;
154     int prog_fd = retrieveProgram(TEST_PROG_PATH);
155     EXPECT_RESULT_OK(configurationMap.writeValue(ACTIVE_MAP_KEY, 0, BPF_ANY));
156 
157     for (int i = 0; i < NUM_SOCKETS; i++) {
158       tds[i] = std::thread(workerThread, prog_fd, &stop);
159     }
160   }
161 
TearDown()162   void TearDown() {
163     SKIP_IF_BPF_NOT_SUPPORTED;
164 
165     // Stop the threads and clean up the program.
166     stop = true;
167     for (int i = 0; i < NUM_SOCKETS; i++) {
168       if (tds[i].joinable()) tds[i].join();
169     }
170     remove(TEST_PROG_PATH);
171     remove(TEST_STATS_MAP_A_PATH);
172     remove(TEST_STATS_MAP_B_PATH);
173     remove(TEST_CONFIGURATION_MAP_PATH);
174   }
175 
swapAndCleanStatsMap(bool expectSynchronized,int seconds)176   void swapAndCleanStatsMap(bool expectSynchronized, int seconds) {
177     uint64_t i = 0;
178     auto test_start = std::chrono::system_clock::now();
179     while ((std::chrono::duration_cast<std::chrono::milliseconds>(
180                 std::chrono::system_clock::now() - test_start)
181                 .count() /
182             1000) < seconds) {
183       // Check if the vacant map is empty based on the current configuration.
184       auto isEmpty = cookieStatsMap[i].isEmpty();
185       ASSERT_RESULT_OK(isEmpty);
186       if (expectSynchronized) {
187         // The map should always be empty because synchronizeKernelRCU should
188         // ensure that the BPF programs running on all cores have seen the write
189         // to the configuration map that tells them to write to the other map.
190         // If it's not empty, fail.
191         ASSERT_TRUE(isEmpty.value())
192             << "Race problem between stats clean and updates";
193       } else if (!isEmpty.value()) {
194         // We found a race condition, which is expected (eventually) because
195         // we're not calling synchronizeKernelRCU. Pass the test.
196         break;
197       }
198 
199       // Change the configuration and wait for rcu grace period.
200       i ^= 1;
201       ASSERT_RESULT_OK(configurationMap.writeValue(ACTIVE_MAP_KEY, i, BPF_ANY));
202       if (expectSynchronized) {
203         EXPECT_EQ(0, synchronizeKernelRCU());
204       }
205 
206       // Clean up the previous map after map swap.
207       EXPECT_RESULT_OK(cookieStatsMap[i].clear());
208     }
209     if (!expectSynchronized) {
210       auto test_end = std::chrono::system_clock::now();
211       auto diffSec = test_end - test_start;
212       auto msec =
213           std::chrono::duration_cast<std::chrono::milliseconds>(diffSec);
214       EXPECT_GE(seconds, (double)(msec.count() / 1000.0))
215           << "Race problem didn't happen before time out";
216     }
217   }
218 };
219 
220 // Verify the race problem disappear when the kernel call synchronize_rcu
221 // after changing the active map.
TEST_F(BpfRaceTest,testRaceWithBarrier)222 TEST_F(BpfRaceTest, testRaceWithBarrier) {
223   SKIP_IF_BPF_NOT_SUPPORTED;
224 
225   swapAndCleanStatsMap(true, 60);
226 }
227 
228 // Confirm the race problem exists when the kernel doesn't call synchronize_rcu
229 // after changing the active map.
TEST_F(BpfRaceTest,testRaceWithoutBarrier)230 TEST_F(BpfRaceTest, testRaceWithoutBarrier) {
231   SKIP_IF_BPF_NOT_SUPPORTED;
232 
233   swapAndCleanStatsMap(false, 60);
234 }
235 
236 }  // namespace android
237