1 #include <cinttypes>
2 #include <cstddef>
3 #include <cstdint>
4 #include <cstdlib>
5 #include <vector>
6
7 #include <lib/storage/storage.h>
8 #include <lib/tipc/tipc.h>
9 #include <lib/unittest/unittest.h>
10 #include <trusty/time.h>
11 #include <trusty_unittest.h>
12
13 #include <binder/IBinder.h>
14 #include <binder/RpcServerTrusty.h>
15 #include <binder/RpcTransportTipcTrusty.h>
16
17 #include <android/hardware/security/see/storage/Availability.h>
18 #include <android/hardware/security/see/storage/CreationMode.h>
19 #include <android/hardware/security/see/storage/FileMode.h>
20 #include <android/hardware/security/see/storage/Filesystem.h>
21 #include <android/hardware/security/see/storage/IFile.h>
22 #include <android/hardware/security/see/storage/ISecureStorage.h>
23 #include <android/hardware/security/see/storage/IStorageSession.h>
24 #include <android/hardware/security/see/storage/Integrity.h>
25 #include <android/hardware/security/see/storage/OpenOptions.h>
26
27 // Needs to be included after AIDL files; definition of ERR_NOT_FOUND macro
28 // breaks the AIDL definitions.
29 #include <uapi/err.h>
30 static const int err_not_found = ERR_NOT_FOUND;
31 #undef ERR_NOT_FOUND
32
33 using ::android::IBinder;
34 using ::android::RpcSession;
35 using ::android::RpcTransportCtxFactoryTipcTrusty;
36 using ::android::sp;
37 using ::android::status_t;
38 using ::android::binder::Status;
39 using ::android::binder::unique_fd;
40 using ::android::hardware::security::see::storage::Availability;
41 using ::android::hardware::security::see::storage::CreationMode;
42 using ::android::hardware::security::see::storage::FileMode;
43 using ::android::hardware::security::see::storage::Filesystem;
44 using ::android::hardware::security::see::storage::IFile;
45 using ::android::hardware::security::see::storage::Integrity;
46 using ::android::hardware::security::see::storage::ISecureStorage;
47 using ::android::hardware::security::see::storage::IStorageSession;
48 using ::android::hardware::security::see::storage::OpenOptions;
49
50 enum class FsType {
51 TP,
52 TDEA,
53 TD,
54 TDP,
55 NSP,
56 };
57
58 struct FsConnection {
59 storage_session_t tipc_session = STORAGE_INVALID_SESSION;
60 sp<IStorageSession> aidl_session = nullptr;
61 };
62
63 static const char aidl_port[] = "com.android.hardware.security.see.storage";
64 static sp<ISecureStorage> aidl_storage = nullptr;
65 static std::array<FsConnection, 5> connections;
66
67 static FsType storage_test_client_fs;
68
69 static const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7};
70
71 static const char tipc_commit_file[] = "test_reconnect_committed_tipc";
72 static const char tipc_nocommit_file[] = "test_reconnect_uncommitted_tipc";
73 static const char aidl_commit_file[] = "test_reconnect_committed_aidl";
74 static const char aidl_nocommit_file[] = "test_reconnect_uncommitted_aidl";
75
76 #define TLOG_TAG "ss-reconnecttest"
77
client_port(FsType fs_type)78 static const char* client_port(FsType fs_type) {
79 switch (fs_type) {
80 case FsType::TP:
81 return STORAGE_CLIENT_TP_PORT;
82 case FsType::TDEA:
83 return STORAGE_CLIENT_TDEA_PORT;
84 case FsType::TD:
85 return STORAGE_CLIENT_TD_PORT;
86 case FsType::TDP:
87 return STORAGE_CLIENT_TDP_PORT;
88 case FsType::NSP:
89 return STORAGE_CLIENT_NSP_PORT;
90 }
91 }
92
client_fs(FsType fs_type,Filesystem * out)93 static bool client_fs(FsType fs_type, Filesystem* out) {
94 switch (fs_type) {
95 case FsType::TP:
96 *out = Filesystem();
97 out->integrity = Integrity::TAMPER_PROOF_AT_REST;
98 out->availability = Availability::AFTER_USERDATA;
99 out->persistent = false;
100 return true;
101 case FsType::TDEA:
102 *out = Filesystem();
103 out->integrity = Integrity::TAMPER_DETECT;
104 out->availability = Availability::BEFORE_USERDATA;
105 out->persistent = false;
106 return true;
107 case FsType::TD:
108 *out = Filesystem();
109 out->integrity = Integrity::TAMPER_DETECT;
110 out->availability = Availability::AFTER_USERDATA;
111 out->persistent = false;
112 return true;
113 case FsType::TDP:
114 *out = Filesystem();
115 out->integrity = Integrity::TAMPER_DETECT;
116 out->availability = Availability::AFTER_USERDATA;
117 out->persistent = true;
118 return true;
119 case FsType::NSP:
120 // AIDL service never accesses NSP currently
121 return false;
122 }
123 }
124
TEST(StorageReconnectBeforeTest,TipcWrite)125 TEST(StorageReconnectBeforeTest, TipcWrite) {
126 int rc;
127 file_handle_t handle;
128 storage_session_t& session =
129 connections[static_cast<size_t>(storage_test_client_fs)]
130 .tipc_session;
131
132 if (session != STORAGE_INVALID_SESSION) {
133 storage_close_session(session);
134 session = STORAGE_INVALID_SESSION;
135 }
136
137 rc = storage_open_session(&session, client_port(storage_test_client_fs));
138 ASSERT_EQ(0, rc);
139
140 // Ensure files doesn't exist.
141 rc = storage_delete_file(session, tipc_commit_file, STORAGE_OP_COMPLETE);
142 rc = (rc == err_not_found) ? 0 : rc;
143 ASSERT_EQ(0, rc);
144 rc = storage_delete_file(session, tipc_nocommit_file, STORAGE_OP_COMPLETE);
145 rc = (rc == err_not_found) ? 0 : rc;
146 ASSERT_EQ(0, rc);
147
148 // Write to file.
149 rc = storage_open_file(
150 session, &handle, tipc_commit_file,
151 STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0);
152 ASSERT_EQ(0, rc);
153
154 rc = storage_write(handle, 0, &data, sizeof(data), STORAGE_OP_COMPLETE);
155 EXPECT_EQ(sizeof(data), rc);
156 storage_close_file(handle);
157
158 // Write to file, but don't commit.
159 rc = storage_open_file(
160 session, &handle, tipc_nocommit_file,
161 STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0);
162 ASSERT_EQ(0, rc);
163
164 rc = storage_write(handle, 0, &data, sizeof(data), 0);
165 EXPECT_EQ(sizeof(data), rc);
166 storage_close_file(handle);
167
168 test_abort:;
169 }
170
TEST(StorageReconnectDuringTest,TipcCheckSessionInvalid)171 TEST(StorageReconnectDuringTest, TipcCheckSessionInvalid) {
172 int rc;
173 storage_session_t& session =
174 connections[static_cast<size_t>(storage_test_client_fs)]
175 .tipc_session;
176
177 // StorageReconnectBeforeTest should have already run
178 ASSERT_NE(STORAGE_INVALID_SESSION, session);
179
180 // Can't commit; storageproxyd disconnected
181 rc = storage_end_transaction(session, true);
182 EXPECT_EQ(ERR_CHANNEL_CLOSED, rc);
183
184 test_abort:;
185 }
186
TEST(StorageReconnectAfterTest,TipcCheckWritten)187 TEST(StorageReconnectAfterTest, TipcCheckWritten) {
188 int rc;
189 file_handle_t handle;
190 storage_session_t& session =
191 connections[static_cast<size_t>(storage_test_client_fs)]
192 .tipc_session;
193
194 // StorageReconnectBeforeTest should have already run
195 ASSERT_NE(STORAGE_INVALID_SESSION, session);
196
197 // Wait so that storage has time to reconnect to storageproxyd
198 storage_session_t temp_session;
199 rc = storage_open_session(&temp_session,
200 client_port(storage_test_client_fs));
201
202 // Attempt to commit write from StorageReconnectBeforeTest
203 rc = storage_end_transaction(session, true);
204 // Fails because storageproxy rebooted and this session was abandoned
205 EXPECT_EQ(ERR_CHANNEL_CLOSED, rc);
206
207 session = temp_session;
208
209 // Read written file and check contents match data
210 static uint8_t buf[sizeof(data)];
211 rc = storage_open_file(session, &handle, tipc_commit_file, 0, 0);
212 ASSERT_EQ(0, rc);
213 rc = storage_read(handle, 0, &buf, sizeof(buf));
214 ASSERT_EQ(sizeof(data), rc);
215 for (size_t i = 0; i < sizeof(data); ++i) {
216 EXPECT_EQ(data[i], buf[i]);
217 }
218
219 // File doesn't exist because creation never committed
220 rc = storage_delete_file(session, tipc_nocommit_file, STORAGE_OP_COMPLETE);
221 EXPECT_EQ(err_not_found, rc);
222
223 storage_close_session(session);
224 session = STORAGE_INVALID_SESSION;
225 test_abort:;
226 }
227
create_exclusive()228 static OpenOptions create_exclusive() {
229 OpenOptions result;
230 result.createMode = CreationMode::CREATE_EXCLUSIVE;
231 result.accessMode = FileMode::READ_WRITE;
232 result.truncateOnOpen = true;
233 return result;
234 }
no_create()235 static OpenOptions no_create() {
236 OpenOptions result;
237 result.createMode = CreationMode::NO_CREATE;
238 result.accessMode = FileMode::READ_WRITE;
239 result.truncateOnOpen = false;
240 return result;
241 }
242
TEST(StorageReconnectBeforeTest,AidlWrite)243 TEST(StorageReconnectBeforeTest, AidlWrite) {
244 auto vec_data = std::vector<uint8_t>(data, data + sizeof(data));
245 sp<IFile> file;
246 int64_t written;
247 Status ret;
248
249 Filesystem client;
250 bool enable_test = client_fs(storage_test_client_fs, &client);
251 if (!enable_test) {
252 goto test_abort;
253 }
254 ASSERT_NE(nullptr, aidl_storage.get());
255
256 {
257 sp<IStorageSession>& aidl_session =
258 connections[static_cast<size_t>(storage_test_client_fs)]
259 .aidl_session;
260
261 if (aidl_session != nullptr) {
262 aidl_session = nullptr;
263 }
264
265 ret = aidl_storage->startSession(client, &aidl_session);
266 ASSERT_EQ(true, ret.isOk());
267
268 // Ensure both files deleted
269 ret = aidl_session->deleteFile(aidl_commit_file);
270 ASSERT_EQ(true,
271 ret.isOk() ||
272 ret.exceptionCode() == Status::EX_SERVICE_SPECIFIC &&
273 ret.serviceSpecificErrorCode() ==
274 ISecureStorage::ERR_NOT_FOUND);
275 ret = aidl_session->deleteFile(aidl_nocommit_file);
276 ASSERT_EQ(true,
277 ret.isOk() ||
278 ret.exceptionCode() == Status::EX_SERVICE_SPECIFIC &&
279 ret.serviceSpecificErrorCode() ==
280 ISecureStorage::ERR_NOT_FOUND);
281
282 // Write and commit
283 ret = aidl_session->openFile(aidl_commit_file, create_exclusive(),
284 &file);
285 ASSERT_EQ(true, ret.isOk());
286 ret = file->write(0, vec_data, &written);
287 ASSERT_EQ(true, ret.isOk());
288 ASSERT_EQ(vec_data.size(), written);
289 ret = aidl_session->commitChanges();
290 ASSERT_EQ(true, ret.isOk());
291
292 // Write but leave uncommitted
293 ret = aidl_session->openFile(aidl_nocommit_file, create_exclusive(),
294 &file);
295 ASSERT_EQ(true, ret.isOk());
296 ret = file->write(0, vec_data, &written);
297 ASSERT_EQ(true, ret.isOk());
298 ASSERT_EQ(vec_data.size(), written);
299 }
300 test_abort:;
301 }
302
TEST(StorageReconnectDuringTest,AidlCheckSessionInvalid)303 TEST(StorageReconnectDuringTest, AidlCheckSessionInvalid) {
304 Status ret;
305 Filesystem client;
306 bool enable_test = client_fs(storage_test_client_fs, &client);
307 if (!enable_test) {
308 goto test_abort;
309 }
310 ASSERT_NE(nullptr, aidl_storage.get());
311
312 {
313 sp<IStorageSession>& aidl_session =
314 connections[static_cast<size_t>(storage_test_client_fs)]
315 .aidl_session;
316 ASSERT_NE(nullptr, aidl_session.get());
317
318 // Session invalid now
319 ret = aidl_session->commitChanges();
320 ASSERT_EQ(Status::EX_TRANSACTION_FAILED, ret.exceptionCode());
321 ASSERT_EQ(android::WOULD_BLOCK, ret.transactionError());
322
323 // Creating a new session on the same filesystem would block
324 sp<IStorageSession> temp_storage;
325 ret = aidl_storage->startSession(client, &temp_storage);
326 ASSERT_EQ(Status::EX_TRANSACTION_FAILED, ret.exceptionCode());
327 ASSERT_EQ(android::WOULD_BLOCK, ret.transactionError());
328 }
329 test_abort:;
330 }
331
TEST(StorageReconnectAfterTest,AidlCheckWritten)332 TEST(StorageReconnectAfterTest, AidlCheckWritten) {
333 sp<IFile> file;
334 Status ret;
335 Filesystem client;
336 std::vector<uint8_t> read_buf;
337 bool enable_test = client_fs(storage_test_client_fs, &client);
338 if (!enable_test) {
339 goto test_abort;
340 }
341 ASSERT_NE(nullptr, aidl_storage.get());
342
343 read_buf.reserve(sizeof(data));
344
345 {
346 // Session is reconnected; commit the uncommitted changes
347 sp<IStorageSession>& aidl_session =
348 connections[static_cast<size_t>(storage_test_client_fs)]
349 .aidl_session;
350 ASSERT_NE(nullptr, aidl_session.get());
351 ret = aidl_session->commitChanges();
352 ASSERT_EQ(true, ret.isOk());
353
354 // Read what was committed in AidlWrite
355 ret = aidl_session->openFile(aidl_commit_file, no_create(), &file);
356 ASSERT_EQ(true, ret.isOk());
357 ret = file->read(sizeof(data), 0, &read_buf);
358 ASSERT_EQ(true, ret.isOk());
359 ASSERT_EQ(sizeof(data), read_buf.size());
360 for (size_t i = 0; i < sizeof(data); ++i) {
361 EXPECT_EQ(data[i], read_buf[i]);
362 }
363
364 // Read what was just committed
365 read_buf.clear();
366 ret = aidl_session->openFile(aidl_nocommit_file, no_create(), &file);
367 ASSERT_EQ(true, ret.isOk());
368 ret = file->read(sizeof(data), 0, &read_buf);
369 ASSERT_EQ(true, ret.isOk());
370 ASSERT_EQ(sizeof(data), read_buf.size());
371 for (size_t i = 0; i < sizeof(data); ++i) {
372 EXPECT_EQ(data[i], read_buf[i]);
373 }
374 }
375 test_abort:;
376 }
377
378 struct storage_unittest {
379 struct unittest unittest;
380 FsType client;
381 const char* run_mode;
382 };
383
run_test(struct unittest * test)384 static bool run_test(struct unittest* test) {
385 struct storage_unittest* storage_test =
386 containerof(test, struct storage_unittest, unittest);
387 storage_test_client_fs = storage_test->client;
388 return RUN_ALL_SUITE_TESTS(storage_test->run_mode);
389 }
390
391 #define PORT_BASE "com.android.storage-reconnect-test."
392
393 #define DEFINE_STORAGE_UNIT_TEST(fs, fs_name, run_mode_val, run_mode_name) \
394 { \
395 .unittest = \
396 { \
397 .port_name = PORT_BASE fs_name run_mode_name, \
398 .run_test = run_test, \
399 }, \
400 .client = (fs), .run_mode = (run_mode_val), \
401 }
402
403 #define DEFINE_STORAGE_UNIT_TESTS_FS(fs, fs_name) \
404 DEFINE_STORAGE_UNIT_TEST((fs), fs_name, "StorageReconnectBeforeTest", \
405 ".before"), \
406 DEFINE_STORAGE_UNIT_TEST((fs), fs_name, \
407 "StorageReconnectDuringTest", ".during"), \
408 DEFINE_STORAGE_UNIT_TEST((fs), fs_name, \
409 "StorageReconnectAfterTest", ".after")
410
main(void)411 int main(void) {
412 static struct storage_unittest storage_unittests[] = {
413 DEFINE_STORAGE_UNIT_TESTS_FS(FsType::NSP, "nsp"),
414 DEFINE_STORAGE_UNIT_TESTS_FS(FsType::TD, "td"),
415 DEFINE_STORAGE_UNIT_TESTS_FS(FsType::TDP, "tdp"),
416 DEFINE_STORAGE_UNIT_TESTS_FS(FsType::TDEA, "tdea"),
417 DEFINE_STORAGE_UNIT_TESTS_FS(FsType::TP, "tp"),
418 };
419 static struct unittest* unittests[countof(storage_unittests)];
420
421 for (size_t i = 0; i < countof(storage_unittests); i++) {
422 unittests[i] = &storage_unittests[i].unittest;
423 }
424
425 int rc = connect(aidl_port, IPC_CONNECT_WAIT_FOR_PORT);
426 if (rc < 0) {
427 TLOGE("Couldn't connect to IStorageService port (%s)\n", aidl_port);
428 return rc;
429 }
430 sp<android::RpcSession> sess =
431 RpcSession::make(RpcTransportCtxFactoryTipcTrusty::make());
432 if (sess == nullptr) {
433 TLOGE("Failed to make RPC session.\n");
434 return ERR_GENERIC;
435 }
436 unique_fd chan_fd;
437 chan_fd.reset(rc);
438 status_t status = sess->setupPreconnectedClient(
439 std::move(chan_fd), []() { return unique_fd(); });
440 if (status != android::OK) {
441 TLOGE("Error (%d) during setupPreconnectedClient\n", status);
442 return ERR_GENERIC;
443 }
444 sp<IBinder> root = sess->getRootObject();
445 if (root == nullptr) {
446 TLOGE("Couldn't get root object.\n");
447 return ERR_GENERIC;
448 }
449
450 aidl_storage = ISecureStorage::asInterface(root);
451 return unittest_main(unittests, countof(unittests));
452 }