1 /*
2  * Copyright (C) 2017 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 "chre/core/event_loop_manager.h"
20 #include "chre/platform/assert.h"
21 #include "chre/platform/log.h"
22 #include "chre/platform/memory.h"
23 #include "chre/platform/shared/nanoapp_dso_util.h"
24 #include "chre/platform/shared/nanoapp_support_lib_dso.h"
25 #include "chre/platform/slpi/memory.h"
26 #include "chre/platform/slpi/power_control_util.h"
27 #include "chre/util/system/debug_dump.h"
28 #include "chre/util/system/napp_permissions.h"
29 #include "chre_api/chre/version.h"
30 
31 #include "dlfcn.h"
32 
33 #include <inttypes.h>
34 #include <string.h>
35 
36 namespace chre {
37 
~PlatformNanoapp()38 PlatformNanoapp::~PlatformNanoapp() {
39   closeNanoapp();
40   if (mAppBinary != nullptr) {
41     memoryFreeBigImage(mAppBinary);
42   }
43 }
44 
start()45 bool PlatformNanoapp::start() {
46   // Invoke the start entry point after successfully opening the app
47   if (!isUimgApp()) {
48     slpiForceBigImage();
49   }
50 
51   return openNanoapp() && mAppInfo->entryPoints.start();
52 }
53 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)54 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
55                                   const void *eventData) {
56   if (!isUimgApp()) {
57     slpiForceBigImage();
58   }
59 
60   mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
61 }
62 
end()63 void PlatformNanoapp::end() {
64   if (!isUimgApp()) {
65     slpiForceBigImage();
66   }
67 
68   mAppInfo->entryPoints.end();
69   closeNanoapp();
70 }
71 
setAppInfo(uint64_t appId,uint32_t appVersion,const char * appFilename,uint32_t targetApiVersion)72 bool PlatformNanoappBase::setAppInfo(uint64_t appId, uint32_t appVersion,
73                                      const char *appFilename,
74                                      uint32_t targetApiVersion) {
75   CHRE_ASSERT(!isLoaded());
76   mExpectedAppId = appId;
77   mExpectedAppVersion = appVersion;
78   mExpectedTargetApiVersion = targetApiVersion;
79   size_t appFilenameLen = strlen(appFilename) + 1;
80   mAppFilename = static_cast<char *>(memoryAllocBigImage(appFilenameLen));
81 
82   bool success = false;
83   if (mAppFilename == nullptr) {
84     LOG_OOM();
85   } else {
86     memcpy(static_cast<void *>(mAppFilename), appFilename, appFilenameLen);
87     success = true;
88   }
89 
90   return success;
91 }
92 
reserveBuffer(uint64_t appId,uint32_t appVersion,uint32_t,size_t appBinaryLen,uint32_t targetApiVersion)93 bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
94                                         uint32_t /* appFlags */,
95                                         size_t appBinaryLen,
96                                         uint32_t targetApiVersion) {
97   CHRE_ASSERT(!isLoaded());
98   bool success = false;
99   constexpr size_t kMaxAppSize = 2 * 1024 * 1024;  // 2 MiB
100 
101   if (appBinaryLen > kMaxAppSize) {
102     LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
103   } else {
104     mAppBinary = memoryAllocBigImage(appBinaryLen);
105     if (mAppBinary == nullptr) {
106       LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
107            appBinaryLen, appId);
108     } else {
109       mExpectedAppId = appId;
110       mExpectedAppVersion = appVersion;
111       mExpectedTargetApiVersion = targetApiVersion;
112       mAppBinaryLen = appBinaryLen;
113       success = true;
114     }
115   }
116 
117   return success;
118 }
119 
copyNanoappFragment(const void * buffer,size_t bufferLen)120 bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
121                                               size_t bufferLen) {
122   CHRE_ASSERT(!isLoaded());
123 
124   bool success = true;
125   if (mBytesLoaded + bufferLen > mAppBinaryLen) {
126     LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
127          bufferLen, mBytesLoaded, mAppBinaryLen);
128     success = false;
129   } else {
130     uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
131     memcpy(binaryBuffer, buffer, bufferLen);
132     mBytesLoaded += bufferLen;
133   }
134 
135   return success;
136 }
137 
loadStatic(const struct chreNslNanoappInfo * appInfo)138 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
139   CHRE_ASSERT(!isLoaded());
140   mIsStatic = true;
141   mAppInfo = appInfo;
142 }
143 
isLoaded() const144 bool PlatformNanoappBase::isLoaded() const {
145   return (mIsStatic ||
146           (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
147           mDsoHandle != nullptr || mAppFilename != nullptr);
148 }
149 
isUimgApp() const150 bool PlatformNanoappBase::isUimgApp() const {
151   return mIsUimgApp;
152 }
153 
closeNanoapp()154 void PlatformNanoappBase::closeNanoapp() {
155   if (mDsoHandle != nullptr) {
156     mAppInfo = nullptr;
157     if (dlclose(mDsoHandle) != 0) {
158       LOGE("dlclose failed: %s", dlerror());
159     }
160     mDsoHandle = nullptr;
161   }
162 }
163 
openNanoapp()164 bool PlatformNanoappBase::openNanoapp() {
165   bool success = false;
166 
167   if (mIsStatic) {
168     success = true;
169   } else if (mAppBinary != nullptr) {
170     success = openNanoappFromBuffer();
171   } else if (mAppFilename != nullptr) {
172     success = openNanoappFromFile();
173   } else {
174     CHRE_ASSERT(false);
175   }
176 
177   // Ensure any allocated memory hanging around is properly cleaned up.
178   if (!success) {
179     closeNanoapp();
180   }
181 
182   // Save this flag locally since it may be referenced while the system is in
183   // micro-image
184   if (mAppInfo != nullptr) {
185     mIsUimgApp = mAppInfo->isTcmNanoapp;
186   }
187 
188   return success;
189 }
190 
openNanoappFromBuffer()191 bool PlatformNanoappBase::openNanoappFromBuffer() {
192   CHRE_ASSERT(mAppBinary != nullptr);
193   CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
194 
195   // Populate a filename string (just a requirement of the dlopenbuf API)
196   constexpr size_t kMaxFilenameLen = 17;
197   char filename[kMaxFilenameLen];
198   snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
199 
200   mDsoHandle = dlopenbuf(filename, static_cast<const char *>(mAppBinary),
201                          static_cast<int>(mAppBinaryLen), RTLD_NOW);
202   memoryFreeBigImage(mAppBinary);
203   mAppBinary = nullptr;
204 
205   return verifyNanoappInfo();
206 }
207 
openNanoappFromFile()208 bool PlatformNanoappBase::openNanoappFromFile() {
209   CHRE_ASSERT(mAppFilename != nullptr);
210   CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
211 
212   mDsoHandle = dlopen(mAppFilename, RTLD_NOW);
213   memoryFreeBigImage(mAppFilename);
214   mAppFilename = nullptr;
215 
216   return verifyNanoappInfo();
217 }
218 
verifyNanoappInfo()219 bool PlatformNanoappBase::verifyNanoappInfo() {
220   bool success = false;
221 
222   if (mDsoHandle == nullptr) {
223     LOGE("No nanoapp info to verify: %s", dlerror());
224   } else {
225     mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
226         dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
227     if (mAppInfo == nullptr) {
228       LOGE("Failed to find app info symbol: %s", dlerror());
229     } else {
230       success = validateAppInfo(mExpectedAppId, mExpectedAppVersion,
231                                 mExpectedTargetApiVersion, mAppInfo);
232       if (!success) {
233         mAppInfo = nullptr;
234       } else {
235         LOGI("Nanoapp loaded: %s (0x%016" PRIx64 ") version 0x%" PRIx32
236              " (%s) uimg %d system %d",
237              mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
238              getAppVersionString(), mAppInfo->isTcmNanoapp,
239              mAppInfo->isSystemNanoapp);
240         if (mAppInfo->structMinorVersion >=
241             CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION) {
242           LOGI("Nanoapp permissions: 0x%" PRIx32, mAppInfo->appPermissions);
243         }
244       }
245     }
246   }
247 
248   return success;
249 }
250 
getAppVersionString() const251 const char *PlatformNanoappBase::getAppVersionString() const {
252   const char *versionString = "<undefined>";
253   if (mAppInfo != nullptr && mAppInfo->structMinorVersion >= 2 &&
254       mAppInfo->appVersionString != NULL) {
255     size_t appVersionStringLength = strlen(mAppInfo->appVersionString);
256 
257     size_t offset = 0;
258     for (size_t i = 0; i < appVersionStringLength; i++) {
259       size_t newOffset = i + 1;
260       if (mAppInfo->appVersionString[i] == '@' &&
261           newOffset < appVersionStringLength) {
262         offset = newOffset;
263         break;
264       }
265     }
266 
267     versionString = &mAppInfo->appVersionString[offset];
268   }
269 
270   return versionString;
271 }
272 
getAppId() const273 uint64_t PlatformNanoapp::getAppId() const {
274   return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
275 }
276 
getAppVersion() const277 uint32_t PlatformNanoapp::getAppVersion() const {
278   return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
279 }
280 
getTargetApiVersion() const281 uint32_t PlatformNanoapp::getTargetApiVersion() const {
282   return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion
283                                : mExpectedTargetApiVersion;
284 }
285 
supportsAppPermissions() const286 bool PlatformNanoapp::supportsAppPermissions() const {
287   return (mAppInfo != nullptr) ? (mAppInfo->structMinorVersion >=
288                                   CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION)
289                                : false;
290 }
291 
getAppPermissions() const292 uint32_t PlatformNanoapp::getAppPermissions() const {
293   return (supportsAppPermissions())
294              ? mAppInfo->appPermissions
295              : static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_NONE);
296 }
297 
getAppName() const298 const char *PlatformNanoapp::getAppName() const {
299   return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
300 }
301 
isSystemNanoapp() const302 bool PlatformNanoapp::isSystemNanoapp() const {
303   // Right now, we assume that system nanoapps are always static nanoapps. Since
304   // mAppInfo can only be null either prior to loading the app (in which case
305   // this function is not expected to return a valid value anyway), or when a
306   // dynamic nanoapp is not running, "false" is the correct return value in that
307   // case.
308   return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
309 }
310 
logStateToBuffer(DebugDumpWrapper & debugDump) const311 void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
312   if (mAppInfo != nullptr) {
313     debugDump.print("%s (%s) @ %s", mAppInfo->name, mAppInfo->vendor,
314                     getAppVersionString());
315   }
316 }
317 
318 }  // namespace chre
319