1 /*
2  * Copyright (C) 2019 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 
17 #include "chre/platform/platform_nanoapp.h"
18 
19 #include <dlfcn.h>
20 #include <cinttypes>
21 
22 #include "chre/platform/assert.h"
23 #include "chre/platform/host_link.h"
24 #include "chre/platform/log.h"
25 #include "chre/platform/shared/authentication.h"
26 #include "chre/platform/shared/nanoapp_dso_util.h"
27 #include "chre/platform/shared/nanoapp_loader.h"
28 #include "chre/util/macros.h"
29 #include "chre/util/system/napp_header_utils.h"
30 #include "chre/util/system/napp_permissions.h"
31 #include "chre_api/chre/version.h"
32 
33 namespace chre {
34 namespace {
35 
36 #ifndef CHRE_NANOAPP_LOAD_ALIGNMENT
37 #define CHRE_NANOAPP_LOAD_ALIGNMENT 0
38 #endif
39 
40 const char kDefaultAppVersionString[] = "<undefined>";
41 size_t kDefaultAppVersionStringSize = ARRAY_SIZE(kDefaultAppVersionString);
42 
43 }  // namespace
44 
~PlatformNanoapp()45 PlatformNanoapp::~PlatformNanoapp() {
46   closeNanoapp();
47 
48   if (mAppBinary != nullptr) {
49     forceDramAccess();
50     nanoappBinaryDramFree(mAppBinary);
51   }
52 }
53 
start()54 bool PlatformNanoapp::start() {
55   //! Always force DRAM access when starting since nanoapps are loaded via DRAM.
56   forceDramAccess();
57 
58   bool success = false;
59   if (!openNanoapp()) {
60     LOGE("Failed to open nanoapp");
61   } else if (mAppInfo == nullptr) {
62     LOGE("Null app info!");
63   } else {
64     success = mAppInfo->entryPoints.start();
65   }
66 
67   return success;
68 }
69 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)70 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
71                                   const void *eventData) {
72   enableDramAccessIfRequired();
73   mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
74 }
75 
end()76 void PlatformNanoapp::end() {
77   enableDramAccessIfRequired();
78   mAppInfo->entryPoints.end();
79   closeNanoapp();
80 }
81 
getAppId() const82 uint64_t PlatformNanoapp::getAppId() const {
83   // TODO (karthikmb/stange): Ideally, we should store the metadata as
84   // variables in TCM, to avoid bumping into DRAM for basic queries.
85   enableDramAccessIfRequired();
86   return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
87 }
88 
getAppVersion() const89 uint32_t PlatformNanoapp::getAppVersion() const {
90   enableDramAccessIfRequired();
91   return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
92 }
93 
supportsAppPermissions() const94 bool PlatformNanoapp::supportsAppPermissions() const {
95   return (mAppInfo != nullptr) ? (mAppInfo->structMinorVersion >=
96                                   CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION_3)
97                                : false;
98 }
99 
getAppPermissions() const100 uint32_t PlatformNanoapp::getAppPermissions() const {
101   return (supportsAppPermissions())
102              ? mAppInfo->appPermissions
103              : static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_NONE);
104 }
105 
getAppName() const106 const char *PlatformNanoapp::getAppName() const {
107   enableDramAccessIfRequired();
108   return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
109 }
110 
getTargetApiVersion() const111 uint32_t PlatformNanoapp::getTargetApiVersion() const {
112   enableDramAccessIfRequired();
113   return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion
114                                : mExpectedTargetApiVersion;
115 }
116 
isSystemNanoapp() const117 bool PlatformNanoapp::isSystemNanoapp() const {
118   enableDramAccessIfRequired();
119   return (mAppInfo != nullptr && mAppInfo->isSystemNanoapp);
120 }
121 
logStateToBuffer(DebugDumpWrapper & debugDump) const122 void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
123   if (mAppInfo != nullptr) {
124     enableDramAccessIfRequired();
125     size_t versionLen = 0;
126     const char *version = getAppVersionString(&versionLen);
127     debugDump.print("%s (%s) @ build: %.*s", mAppInfo->name, mAppInfo->vendor,
128                     static_cast<int>(versionLen), version);
129   }
130 }
131 
getAppVersionString(size_t * length) const132 const char *PlatformNanoappBase::getAppVersionString(size_t *length) const {
133   const char *versionString = kDefaultAppVersionString;
134   *length = kDefaultAppVersionStringSize;
135   enableDramAccessIfRequired();
136 
137   if (mAppUnstableId != nullptr) {
138     size_t appVersionStringLength = strlen(mAppUnstableId);
139 
140     //! The unstable ID is expected to be in the format of
141     //! <descriptor>=<nanoapp_name>@<build_id>. Use this expected layout
142     //! knowledge to parse the string and only return the build ID portion that
143     //! should be printed.
144     size_t startOffset = appVersionStringLength;
145     for (size_t i = 0; i < appVersionStringLength; i++) {
146       size_t offset = i + 1;
147       if (startOffset == appVersionStringLength && mAppUnstableId[i] == '@') {
148         startOffset = offset;
149       }
150     }
151 
152     if (startOffset < appVersionStringLength) {
153       versionString = &mAppUnstableId[startOffset];
154       *length = appVersionStringLength - startOffset;
155     }
156   }
157 
158   return versionString;
159 }
160 
isLoaded() const161 bool PlatformNanoappBase::isLoaded() const {
162   return (mIsStatic ||
163           (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
164           mDsoHandle != nullptr || mAppFilename != nullptr);
165 }
166 
isTcmApp() const167 bool PlatformNanoappBase::isTcmApp() const {
168   return mIsTcmNanoapp;
169 }
170 
loadStatic(const struct chreNslNanoappInfo * appInfo)171 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
172   CHRE_ASSERT(!isLoaded());
173   mIsStatic = true;
174   mAppInfo = appInfo;
175 }
176 
reserveBuffer(uint64_t appId,uint32_t appVersion,uint32_t appFlags,size_t appBinaryLen,uint32_t targetApiVersion)177 bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
178                                         uint32_t appFlags, size_t appBinaryLen,
179                                         uint32_t targetApiVersion) {
180   CHRE_ASSERT(!isLoaded());
181 
182   forceDramAccess();
183 
184   bool success = false;
185   mAppBinary =
186       nanoappBinaryDramAlloc(appBinaryLen, CHRE_NANOAPP_LOAD_ALIGNMENT);
187 
188   bool isSigned = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_SIGNED);
189   if (!isSigned) {
190     LOGE("Unable to load unsigned nanoapps");
191   } else if (mAppBinary == nullptr) {
192     LOG_OOM();
193   } else {
194     bool tcmCapable = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_TCM_CAPABLE);
195     mExpectedAppId = appId;
196     mExpectedAppVersion = appVersion;
197     mExpectedTargetApiVersion = targetApiVersion;
198     mExpectedTcmCapable = tcmCapable;
199     mAppBinaryLen = appBinaryLen;
200     success = true;
201   }
202 
203   return success;
204 }
205 
copyNanoappFragment(const void * buffer,size_t bufferLen)206 bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
207                                               size_t bufferLen) {
208   CHRE_ASSERT(!isLoaded());
209 
210   forceDramAccess();
211 
212   bool success = true;
213 
214   if ((mBytesLoaded + bufferLen) > mAppBinaryLen) {
215     LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
216          bufferLen, mBytesLoaded, mAppBinaryLen);
217     success = false;
218   } else {
219     uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
220     memcpy(binaryBuffer, buffer, bufferLen);
221     mBytesLoaded += bufferLen;
222   }
223 
224   return success;
225 }
226 
verifyNanoappInfo()227 bool PlatformNanoappBase::verifyNanoappInfo() {
228   bool success = false;
229 
230   if (mDsoHandle == nullptr) {
231     LOGE("No nanoapp info to verify");
232   } else {
233     mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
234         dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
235     if (mAppInfo == nullptr) {
236       LOGE("Failed to find app info symbol");
237     } else {
238       mAppUnstableId = mAppInfo->appVersionString;
239       if (mAppUnstableId == nullptr) {
240         LOGE("Failed to find unstable ID symbol");
241       } else {
242         success = validateAppInfo(mExpectedAppId, mExpectedAppVersion,
243                                   mExpectedTargetApiVersion, mAppInfo);
244         if (success && mAppInfo->isTcmNanoapp != mExpectedTcmCapable) {
245           success = false;
246           LOGE("Expected TCM nanoapp %d found %d", mExpectedTcmCapable,
247                mAppInfo->isTcmNanoapp);
248         }
249 
250         if (!success) {
251           mAppInfo = nullptr;
252         } else {
253           LOGI("Nanoapp loaded: %s (0x%016" PRIx64 ") version 0x%" PRIx32
254                " (%s) uimg %d system %d",
255                mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
256                mAppInfo->appVersionString, mAppInfo->isTcmNanoapp,
257                mAppInfo->isSystemNanoapp);
258           if (mAppInfo->structMinorVersion >=
259               CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION_3) {
260             LOGI("Nanoapp permissions: 0x%" PRIx32, mAppInfo->appPermissions);
261           }
262         }
263       }
264     }
265   }
266   return success;
267 }
268 
sendTokenDatabaseInfo()269 void PlatformNanoappBase::sendTokenDatabaseInfo() {
270   auto *loader = reinterpret_cast<chre::NanoappLoader *>(mDsoHandle);
271   uint32_t databaseOffset = 0;
272   size_t databaseSize = 0;
273 
274   loader->getTokenDatabaseSectionInfo(&databaseOffset, &databaseSize);
275   HostLinkBase::sendNanoappTokenDatabaseInfo(mExpectedAppId, databaseOffset,
276                                              databaseSize);
277 }
278 
openNanoapp()279 bool PlatformNanoappBase::openNanoapp() {
280   bool success = false;
281   if (mIsStatic) {
282     success = true;
283   } else if (mAppBinary != nullptr) {
284     //! The true start of the binary will be after the authentication header.
285     //! Use the returned value from authenticateBinary to ensure dlopenbuf has
286     //! the starting address to a valid ELF.
287     void *binaryStart = mAppBinary;
288     if (!authenticateBinary(mAppBinary, mAppBinaryLen, &binaryStart)) {
289       LOGE("Unable to authenticate 0x%" PRIx64 " not loading", mExpectedAppId);
290     } else if (mDsoHandle != nullptr) {
291       LOGE("Trying to reopen an existing buffer");
292     } else {
293       mDsoHandle = dlopenbuf(binaryStart, mExpectedTcmCapable);
294       success = verifyNanoappInfo();
295       if (success) {
296         sendTokenDatabaseInfo();
297       }
298     }
299   }
300 
301   if (!success) {
302     closeNanoapp();
303   }
304 
305   if (mAppBinary != nullptr) {
306     nanoappBinaryDramFree(mAppBinary);
307     mAppBinary = nullptr;
308   }
309 
310   // Save this flag locally since it may be referenced while the system is in
311   // TCM-only mode.
312   if (mAppInfo != nullptr) {
313     mIsTcmNanoapp = mAppInfo->isTcmNanoapp;
314   }
315 
316   return success;
317 }
318 
closeNanoapp()319 void PlatformNanoappBase::closeNanoapp() {
320   if (mDsoHandle != nullptr) {
321     // Force DRAM access since dl* functions are only safe to call with DRAM
322     // available.
323     forceDramAccess();
324     mAppInfo = nullptr;
325     if (dlclose(mDsoHandle) != 0) {
326       LOGE("dlclose failed");
327     }
328     mDsoHandle = nullptr;
329   }
330 }
331 
332 }  // namespace chre
333