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