1 /*
2 * Copyright (C) 2022 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 required 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 #include <errno.h>
17 #include <getopt.h>
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/uio.h>
25 #include <unistd.h>
26
27 #include <condition_variable>
28 #include <cstddef>
29 #include <mutex>
30 #include <queue>
31
32 #include <android-base/expected.h>
33 #include <android-base/logging.h>
34 #include <android/frameworks/stats/BnStats.h>
35 #include <android/frameworks/stats/IStats.h>
36 #include <android/trusty/stats/nw/setter/IStatsSetter.h>
37 #include <binder/RpcServer.h>
38 #include <binder/RpcSession.h>
39 #include <binder/RpcTransportRaw.h>
40 #include <binder/RpcTransportTipcAndroid.h>
41 #include <binder/RpcTrusty.h>
42 #include <trusty/tipc.h>
43
44 /** DOC:
45 * ./build-root/build-qemu-generic-arm64-test-debug/run \
46 * --android $ANDROID_PROJECT_ROOT \
47 * --headless --shell-command \
48 * "/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test"
49 *
50 * adb -s emulator-5554 shell \
51 * /data/nativetest64/vendor/trusty_stats_test/trusty_stats_test
52 */
53 using ::android::base::unique_fd;
54 using ::android::binder::Status;
55 using ::android::frameworks::stats::BnStats;
56 using ::android::frameworks::stats::IStats;
57 using ::android::frameworks::stats::VendorAtom;
58 using ::android::frameworks::stats::VendorAtomValue;
59 using ::android::trusty::stats::nw::setter::IStatsSetter;
60
61 constexpr const char kTrustyDefaultDeviceName[] = "/dev/trusty-ipc-dev0";
62 constexpr const char kTrustyStatsSetterTest[] =
63 "com.android.frameworks.stats.trusty.test.relayer.istats_setter";
64 constexpr const char kTrustyStatsSetterMetrics[] =
65 "com.android.frameworks.stats.trusty.metrics.istats_setter";
66 constexpr const char kTrustyStatsPortTest[] = "com.android.trusty.stats.test";
67 constexpr const char kTrustyCrashPortTest[] = "com.android.trusty.crashtest";
68 constexpr const char kTrustyCrasherUuid[] = "7ee4dddc-177a-420a-96ea-5d413d88228e:crasher";
69
70 enum TrustyAtoms : int32_t {
71 TrustyAppCrashed = 100072,
72 TrustyError = 100145,
73 TrustyStorageError = 100146
74 };
75
76 enum TestMsgHeader : int32_t {
77 TEST_PASSED = 0,
78 TEST_FAILED = 1,
79 TEST_MESSAGE = 2,
80 };
81
82 namespace android {
83 namespace trusty {
84 namespace stats {
85
86 class Stats : public BnStats {
87 public:
Stats()88 Stats() : BnStats() {}
89
reportVendorAtom(const VendorAtom & vendorAtom)90 Status reportVendorAtom(const VendorAtom& vendorAtom) override {
91 const char* atomIdStr = vendorAtomStr(vendorAtom.atomId);
92 ALOGD("Vendor atom reported of type: %s\n", atomIdStr);
93 std::lock_guard lock(mLock);
94 mQueueVendorAtom.push(vendorAtom);
95 mCondVar.notify_one();
96 return Status::ok();
97 }
98
getVendorAtom(VendorAtom * pVendorAtom,int64_t waitForMs)99 status_t getVendorAtom(VendorAtom* pVendorAtom, int64_t waitForMs) {
100 std::unique_lock lock(mLock);
101 while (mQueueVendorAtom.empty()) {
102 auto rc = mCondVar.wait_for(lock, std::chrono::milliseconds(waitForMs));
103 if (rc == std::cv_status::timeout) {
104 return TIMED_OUT;
105 }
106 }
107 *pVendorAtom = mQueueVendorAtom.front();
108 mQueueVendorAtom.pop();
109 return NO_ERROR;
110 }
111
112 private:
vendorAtomStr(int32_t atomId)113 const char* vendorAtomStr(int32_t atomId) {
114 switch (atomId) {
115 case TrustyAtoms::TrustyAppCrashed:
116 return "TrustyAtoms::TrustyAppCrashed";
117 case TrustyAtoms::TrustyError:
118 return "TrustyAtoms::TrustyError";
119 case TrustyAtoms::TrustyStorageError:
120 return "TrustyAtoms::TrustyStorageError";
121 default:
122 return "unknown TrustyAtoms type";
123 }
124 }
125 std::mutex mLock;
126 std::condition_variable mCondVar;
127 std::queue<VendorAtom> mQueueVendorAtom;
128 };
129
130 class TrustyStatsTestBase : public ::testing::Test {
131 protected:
TrustyStatsTestBase(std::string && portNameStatsSetter,std::string && portNamePortTest)132 TrustyStatsTestBase(std::string&& portNameStatsSetter, std::string&& portNamePortTest)
133 : mPortTestFd(-1),
134 mPortNameStatsSetter(std::move(portNameStatsSetter)),
135 mPortNamePortTest(std::move(portNamePortTest)) {}
136
SetUp()137 void SetUp() override {
138 // Commenting out the server portion because we do not have any direct
139 // incoming call Calls from TA are currently being handled on the mSession's
140 // extra thread. android::sp<::android::RpcServer> server =
141 // ::android::RpcServer::make(::android::RpcTransportCtxFactoryRaw::make());
142
143 mStats = android::sp<Stats>::make();
144 // Increasing number of incoming threads on mSession to be able to receive
145 // callbacks
146 auto session_initializer = [](sp<RpcSession>& session) {
147 session->setMaxIncomingThreads(1);
148 };
149
150 ASSERT_FALSE(mSession);
151 mSession = RpcTrustyConnectWithSessionInitializer(
152 kTrustyDefaultDeviceName, mPortNameStatsSetter.c_str(), session_initializer);
153 ASSERT_TRUE(mSession);
154
155 auto root = mSession->getRootObject();
156 ASSERT_TRUE(root);
157 auto statsSetter = IStatsSetter::asInterface(root);
158 ASSERT_TRUE(statsSetter);
159 statsSetter->setInterface(mStats);
160 }
TearDown()161 void TearDown() override {
162 // close connection to unitest app
163 if (mPortTestFd != -1) {
164 tipc_close(mPortTestFd);
165 }
166 mPortTestFd = -1;
167
168 if (mSession) {
169 // shutdownAndWait here races with sending out the DecStrong
170 // messages after reportVendorAtom returns, so we delay it a little
171 // bit to give the messages time to go out over the transport
172 usleep(50000);
173 ASSERT_TRUE(mSession->shutdownAndWait(true));
174 }
175 mSession.clear();
176 mStats.clear();
177 }
StartPortTest()178 void StartPortTest() {
179 // connect to unitest app
180 mPortTestFd = tipc_connect(kTrustyDefaultDeviceName, mPortNamePortTest.c_str());
181 if (mPortTestFd < 0) {
182 ALOGE("Failed to connect to '%s' app: %s\n", kTrustyStatsPortTest,
183 strerror(-mPortTestFd));
184 }
185 ASSERT_GT(mPortTestFd, 0);
186 }
WaitPortTestDone()187 void WaitPortTestDone() {
188 // wait for test to complete
189 char rxBuf[1024];
190 const char prolog[] = "Trusty PORT_TEST:";
191 strncpy(rxBuf, prolog, sizeof(prolog) - 1);
192 char* pRxBuf = rxBuf + sizeof(prolog) - 1;
193 size_t remainingBufSize = sizeof(rxBuf) - sizeof(prolog) - 1;
194
195 ASSERT_NE(mPortTestFd, -1);
196 for (;;) {
197 int rc = read(mPortTestFd, pRxBuf, remainingBufSize);
198 ASSERT_GT(rc, 0);
199 ASSERT_LT(rc, (int)remainingBufSize);
200 if (pRxBuf[0] == TEST_PASSED) {
201 break;
202 } else if (pRxBuf[0] == TEST_FAILED) {
203 break;
204 } else if (pRxBuf[0] == TEST_MESSAGE) {
205 pRxBuf[0] = ' ';
206 write(STDOUT_FILENO, rxBuf, rc + sizeof(prolog) - 1);
207 } else {
208 ALOGE("Bad message header: %d\n", rxBuf[0]);
209 break;
210 }
211 }
212 ASSERT_EQ(pRxBuf[0], TEST_PASSED);
213 }
214
215 android::sp<Stats> mStats;
216
217 private:
218 android::sp<RpcSession> mSession;
219 int mPortTestFd;
220 std::string mPortNameStatsSetter;
221 std::string mPortNamePortTest;
222 };
223
224 class TrustyStatsTest : public TrustyStatsTestBase {
225 protected:
TrustyStatsTest()226 TrustyStatsTest() : TrustyStatsTestBase(kTrustyStatsSetterTest, kTrustyStatsPortTest) {}
227 };
228
229 class TrustyMetricsCrashTest : public TrustyStatsTestBase {
230 protected:
TrustyMetricsCrashTest()231 TrustyMetricsCrashTest()
232 : TrustyStatsTestBase(kTrustyStatsSetterMetrics, kTrustyCrashPortTest) {}
233 };
234
TEST_F(TrustyStatsTest,CheckAtoms)235 TEST_F(TrustyStatsTest, CheckAtoms) {
236 int atomAppCrashedCnt = 0;
237 int atomStorageErrorCnt = 0;
238 int atomTrustyErrorCnt = 0;
239 uint64_t blockForMs = 500;
240 StartPortTest();
241 WaitPortTestDone();
242 for (;;) {
243 VendorAtom vendorAtom;
244 auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);
245 ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));
246 if (status == TIMED_OUT) {
247 // No more atoms
248 break;
249 }
250
251 ASSERT_THAT(vendorAtom.atomId,
252 ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
253 ::testing::Eq(TrustyAtoms::TrustyError),
254 ::testing::Eq(TrustyAtoms::TrustyStorageError)));
255 ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
256 switch (vendorAtom.atomId) {
257 case TrustyAtoms::TrustyAppCrashed:
258 ++atomAppCrashedCnt;
259 ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
260 "5247d19b-cf09-4272-a450-3ef20dbefc14");
261 break;
262 case TrustyAtoms::TrustyStorageError:
263 ++atomStorageErrorCnt;
264 ASSERT_EQ(vendorAtom.values[0].get<VendorAtomValue::intValue>(), 5);
265 ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
266 "5247d19b-cf09-4272-a450-3ef20dbefc14");
267 ASSERT_EQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
268 "5247d19b-cf09-4272-a450-3ef20dbefc14");
269 ASSERT_EQ(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);
270 ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);
271 ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),
272 0x4BCDEFABBAFEDCBALL);
273 ASSERT_EQ(vendorAtom.values[6].get<VendorAtomValue::intValue>(), 4);
274 ASSERT_EQ(vendorAtom.values[7].get<VendorAtomValue::longValue>(), 1023);
275 break;
276 case TrustyAtoms::TrustyError:
277 ++atomTrustyErrorCnt;
278 break;
279 default:
280 FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
281 break;
282 }
283 };
284 ASSERT_EQ(atomAppCrashedCnt, 1);
285 ASSERT_EQ(atomStorageErrorCnt, 1);
286 ASSERT_EQ(atomTrustyErrorCnt, 0);
287 }
288
TEST_F(TrustyMetricsCrashTest,CheckTrustyCrashAtoms)289 TEST_F(TrustyMetricsCrashTest, CheckTrustyCrashAtoms) {
290 const std::vector<uint32_t> kExpectedCrashReasonsArm64{
291 0x00000001U, // exit_failure (twice)
292 0x00000001U,
293 0x92000004U, // read_null_ptr
294 0xf200002aU, // brk_instruction
295 0x92000004U, // read_bad_ptr
296 0x92000044U, // crash_write_bad_ptr
297 0x9200004fU, // crash_write_ro_ptr
298 0x8200000fU, // crash_exec_rodata
299 0x8200000fU, // crash_exec_data
300 };
301 const std::vector<uint32_t> kExpectedCrashReasonsArm32{
302 0x00000001U, // exit_failure (twice)
303 0x00000001U,
304 0x20000007U, // read_null_ptr
305 0x20000007U, // read_bad_ptr
306 0x20000807U, // crash_write_bad_ptr
307 0x2000080fU, // crash_write_ro_ptr
308 0x3000000fU, // crash_exec_rodata
309 0x3000000fU, // crash_exec_data
310 };
311
312 int expectedAtomCnt = 7;
313 int atomAppCrashedCnt = 0;
314 int atomStorageErrorCnt = 0;
315 int atomTrustyErrorCnt = 0;
316 std::vector<uint32_t> atomCrashReasons;
317 uint64_t blockForMs = 500;
318 StartPortTest();
319 WaitPortTestDone();
320 for (;;) {
321 VendorAtom vendorAtom;
322 auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);
323 ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));
324 if (status == TIMED_OUT) {
325 // No more atoms
326 break;
327 }
328
329 ASSERT_THAT(vendorAtom.atomId,
330 ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
331 ::testing::Eq(TrustyAtoms::TrustyError),
332 ::testing::Eq(TrustyAtoms::TrustyStorageError)));
333 ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
334
335 switch (vendorAtom.atomId) {
336 case TrustyAtoms::TrustyAppCrashed:
337 ++atomAppCrashedCnt;
338 ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
339 kTrustyCrasherUuid);
340 atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());
341 break;
342 case TrustyAtoms::TrustyStorageError:
343 ++atomStorageErrorCnt;
344 break;
345 case TrustyAtoms::TrustyError:
346 ++atomTrustyErrorCnt;
347 ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
348 break;
349 default:
350 FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
351 }
352 }
353 ASSERT_GE(atomAppCrashedCnt, expectedAtomCnt - 1);
354 ASSERT_EQ(atomStorageErrorCnt, 0);
355 // There is one dropped event left over from Trusty boot,
356 // it may show up here
357 ASSERT_LE(atomTrustyErrorCnt, 1);
358 ASSERT_THAT(atomCrashReasons,
359 ::testing::AnyOf(kExpectedCrashReasonsArm64, kExpectedCrashReasonsArm32));
360 };
361
362 } // namespace stats
363 } // namespace trusty
364 } // namespace android
365