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/platform/assert.h"
20 #include "chre/platform/log.h"
21 #include "chre/platform/memory.h"
22 #include "chre/platform/shared/nanoapp_support_lib_dso.h"
23 #include "chre_api/chre/version.h"
24 
25 #include "dlfcn.h"
26 
27 #include <inttypes.h>
28 #include <string.h>
29 
30 namespace chre {
31 
32 namespace {
33 
34 /**
35  * Performs sanity checks on the app info structure included in a dynamically
36  * loaded nanoapp.
37  *
38  * @param expectedAppId
39  * @param expectedAppVersion
40  * @param appInfo
41  *
42  * @return true if validation was successful
43  */
validateAppInfo(uint64_t expectedAppId,uint32_t expectedAppVersion,const struct chreNslNanoappInfo * appInfo)44 bool validateAppInfo(uint64_t expectedAppId, uint32_t expectedAppVersion,
45                      const struct chreNslNanoappInfo *appInfo) {
46   uint32_t ourApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(chreGetApiVersion());
47   uint32_t targetApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(
48       appInfo->targetApiVersion);
49 
50   bool success = false;
51   if (appInfo->magic != CHRE_NSL_NANOAPP_INFO_MAGIC) {
52     LOGE("Invalid app info magic: got 0x%08" PRIx32 " expected 0x%08" PRIx32,
53          appInfo->magic, CHRE_NSL_NANOAPP_INFO_MAGIC);
54   } else if (appInfo->appId == 0) {
55     LOGE("Rejecting invalid app ID 0");
56   } else if (expectedAppId != appInfo->appId) {
57     LOGE("Expected app ID (0x%016" PRIx64 ") doesn't match internal one (0x%016"
58          PRIx64 ")", expectedAppId, appInfo->appId);
59   } else if (expectedAppVersion != appInfo->appVersion) {
60     LOGE("Expected app version (0x%" PRIx32 ") doesn't match internal one (0x%"
61          PRIx32 ")", expectedAppVersion, appInfo->appVersion);
62   } else if (targetApiMajorVersion != ourApiMajorVersion) {
63     LOGE("App targets a different major API version (%" PRIu32 ") than what we "
64          "provide (%" PRIu32 ")", targetApiMajorVersion, ourApiMajorVersion);
65   } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
66     LOGE("App name is too long");
67   } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
68     LOGE("App vendor is too long");
69   } else {
70     success = true;
71   }
72 
73   return success;
74 }
75 
76 }  // anonymous namespace
77 
~PlatformNanoapp()78 PlatformNanoapp::~PlatformNanoapp() {
79   closeNanoapp();
80   if (mAppBinary != nullptr) {
81     memoryFree(mAppBinary);
82   }
83 }
84 
start()85 bool PlatformNanoapp::start() {
86   // Invoke the start entry point after successfully opening the app
87   return (mIsStatic || openNanoapp()) ? mAppInfo->entryPoints.start() : false;
88 }
89 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)90 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId,
91                                   uint16_t eventType,
92                                   const void *eventData) {
93   mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
94 }
95 
end()96 void PlatformNanoapp::end() {
97   mAppInfo->entryPoints.end();
98   closeNanoapp();
99 }
100 
loadFromBuffer(uint64_t appId,uint32_t appVersion,const void * appBinary,size_t appBinaryLen)101 bool PlatformNanoappBase::loadFromBuffer(uint64_t appId, uint32_t appVersion,
102                                          const void *appBinary,
103                                          size_t appBinaryLen) {
104   CHRE_ASSERT(!isLoaded());
105   bool success = false;
106   constexpr size_t kMaxAppSize = 2 * 1024 * 1024;  // 2 MiB
107 
108   if (appBinaryLen > kMaxAppSize) {
109     LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
110   } else {
111     mAppBinary = memoryAlloc(appBinaryLen);
112     if (mAppBinary == nullptr) {
113       LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
114            appBinaryLen, appId);
115     } else {
116       mExpectedAppId = appId;
117       mExpectedAppVersion = appVersion;
118       mAppBinaryLen = appBinaryLen;
119       memcpy(mAppBinary, appBinary, appBinaryLen);
120       success = true;
121     }
122   }
123 
124   return success;
125 }
126 
loadStatic(const struct chreNslNanoappInfo * appInfo)127 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
128   CHRE_ASSERT(!isLoaded());
129   mIsStatic = true;
130   mAppInfo = appInfo;
131 }
132 
isLoaded() const133 bool PlatformNanoappBase::isLoaded() const {
134   return (mIsStatic || mAppBinary != nullptr);
135 }
136 
closeNanoapp()137 void PlatformNanoappBase::closeNanoapp() {
138   if (mDsoHandle != nullptr) {
139     if (dlclose(mDsoHandle) != 0) {
140       const char *name = (mAppInfo != nullptr) ? mAppInfo->name : "unknown";
141       LOGE("dlclose of %s failed: %s", name, dlerror());
142     }
143     mDsoHandle = nullptr;
144   }
145 }
146 
openNanoapp()147 bool PlatformNanoappBase::openNanoapp() {
148   bool success = false;
149 
150   // Populate a filename string (just a requirement of the dlopenbuf API)
151   constexpr size_t kMaxFilenameLen = 17;
152   char filename[kMaxFilenameLen];
153   snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
154 
155   CHRE_ASSERT(mAppBinary != nullptr);
156   CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
157   mDsoHandle = dlopenbuf(
158       filename, static_cast<const char *>(mAppBinary),
159       static_cast<int>(mAppBinaryLen), RTLD_NOW);
160   if (mDsoHandle == nullptr) {
161     LOGE("Failed to load nanoapp: %s", dlerror());
162   } else {
163     mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
164         dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
165     if (mAppInfo == nullptr) {
166       LOGE("Failed to find app info symbol: %s", dlerror());
167     } else {
168       success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
169       if (!success) {
170         mAppInfo = nullptr;
171       } else {
172         LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64 ") version 0x%"
173              PRIx32, mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion);
174       }
175     }
176   }
177 
178   return success;
179 }
180 
getAppId() const181 uint64_t PlatformNanoapp::getAppId() const {
182   return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
183 }
184 
getAppVersion() const185 uint32_t PlatformNanoapp::getAppVersion() const {
186   return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
187 }
188 
getTargetApiVersion() const189 uint32_t PlatformNanoapp::getTargetApiVersion() const {
190   return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
191 }
192 
isSystemNanoapp() const193 bool PlatformNanoapp::isSystemNanoapp() const {
194   // Right now, we assume that system nanoapps are always static nanoapps. Since
195   // mAppInfo can only be null either prior to loading the app (in which case
196   // this function is not expected to return a valid value anyway), or when a
197   // dynamic nanoapp is not running, "false" is the correct return value in that
198   // case.
199   return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
200 }
201 
202 }  // namespace chre
203