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 }