• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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