1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ashmem.h"
17
18 #include <cerrno>
19 #include <cstdio>
20 #include <string>
21 #include <fcntl.h>
22 #include <linux/ashmem.h>
23 #include <pthread.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/syscall.h>
28 #include <sys/sysmacros.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <dlfcn.h>
32 #include "securec.h"
33 #include "utils_log.h"
34
35 namespace OHOS {
36 static pthread_mutex_t g_ashmemLock = PTHREAD_MUTEX_INITIALIZER;
37
38 using openFdFunction = int (*)();
39 static openFdFunction g_openFdApi = nullptr;
40
ProbeAshmemFdFunction()41 openFdFunction ProbeAshmemFdFunction()
42 {
43 auto handle = dlopen("libashmemd_client.so", RTLD_NOW);
44 if (!handle) {
45 return nullptr;
46 }
47 openFdFunction func = reinterpret_cast<openFdFunction>(dlsym(handle, "openAshmemdFd"));
48 if (!func) {
49 dlclose(handle);
50 }
51 return func;
52 }
53
TrimFromStart(std::string & s)54 static inline void TrimFromStart(std::string &s)
55 {
56 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {return !std::isspace(ch);}));
57 }
58
TrimFromEnd(std::string & s)59 static inline void TrimFromEnd(std::string &s)
60 {
61 s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {return !std::isspace(ch);}).base(), s.end());
62 }
63
Trim(std::string & s)64 static void Trim(std::string &s)
65 {
66 TrimFromStart(s);
67 TrimFromEnd(s);
68 }
69
GetAshmemDeviceFd()70 static int GetAshmemDeviceFd()
71 {
72 static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
73 std::string boot_id;
74 char buf[BUFSIZ];
75 ssize_t n;
76 int fdBoot = -1;
77 int fdAshmem = -1;
78
79 fdBoot = TEMP_FAILURE_RETRY(open(boot_id_path.c_str(), O_RDONLY | O_CLOEXEC));
80 if (fdBoot < 0) {
81 return -1;
82 }
83
84 if ((n = TEMP_FAILURE_RETRY(read(fdBoot, &buf[0], sizeof(buf)))) > 0) {
85 boot_id.append(buf, n);
86 } else {
87 close(fdBoot);
88 return -1;
89 }
90 close(fdBoot);
91
92 Trim(boot_id);
93
94 fdAshmem = TEMP_FAILURE_RETRY(open(("/dev/ashmem" + boot_id).c_str(), O_RDWR | O_CLOEXEC));
95 return fdAshmem;
96 }
97
98
AshmemOpenLocked()99 static int AshmemOpenLocked()
100 {
101 int fd = -1;
102
103 if (g_openFdApi == nullptr) {
104 g_openFdApi = ProbeAshmemFdFunction();
105 }
106
107 if (g_openFdApi != nullptr) {
108 fd = g_openFdApi();
109 } else {
110 fd = GetAshmemDeviceFd();
111 }
112
113 if (fd < 0) {
114 fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
115 }
116
117 if (fd < 0) {
118 UTILS_LOGE("%{public}s: fd is invalid, fd = %{public}d", __func__, fd);
119 return fd;
120 }
121
122 struct stat st;
123 int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
124 if (ret < 0) {
125 UTILS_LOGE("%{public}s: Failed to exec fstat, ret = %{public}d", __func__, ret);
126 close(fd);
127 return ret;
128 }
129
130 if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
131 UTILS_LOGE("%{public}s: stat status is invalid, st_mode = %{public}u", __func__, st.st_mode);
132 close(fd);
133 return -1;
134 }
135 return fd;
136 }
137
AshmemOpen()138 static int AshmemOpen()
139 {
140 pthread_mutex_lock(&g_ashmemLock);
141 int fd = AshmemOpenLocked();
142 pthread_mutex_unlock(&g_ashmemLock);
143 return fd;
144 }
145
146 /*
147 * AshmemCreate - create a new ashmem region and returns the file descriptor
148 * fd < 0 means failed
149 *
150 */
AshmemCreate(const char * name,size_t size)151 int AshmemCreate(const char *name, size_t size)
152 {
153 int ret;
154 int fd = AshmemOpen();
155 if (fd < 0) {
156 UTILS_LOGE("%{public}s: Failed to exec AshmemOpen fd = %{public}d", __func__, fd);
157 return fd;
158 }
159
160 if (name != nullptr) {
161 char buf[ASHMEM_NAME_LEN] = {0};
162 ret = strcpy_s(buf, sizeof(buf), name);
163 if (ret != EOK) {
164 UTILS_LOGE("%{public}s: Failed to exec strcpy_s, name= %{public}s, ret= %{public}d", __func__, name, ret);
165 close(fd);
166 return -1;
167 }
168 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
169 if (ret < 0) {
170 UTILS_LOGE("%{public}s: Failed to set name, name= %{public}s, ret= %{public}d", __func__, name, ret);
171 close(fd);
172 return ret;
173 }
174 }
175
176 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
177 if (ret < 0) {
178 UTILS_LOGE("%{public}s: Failed to set size, size= %{public}zu", __func__, size);
179 close(fd);
180 return ret;
181 }
182 return fd;
183 }
184
AshmemSetProt(int fd,int prot)185 int AshmemSetProt(int fd, int prot)
186 {
187 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
188 }
189
AshmemGetSize(int fd)190 int AshmemGetSize(int fd)
191 {
192 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
193 }
194
Ashmem(int fd,int size)195 Ashmem::Ashmem(int fd, int size) : memoryFd_(fd), memorySize_(size), flag_(0), startAddr_(nullptr)
196 {
197 }
198
~Ashmem()199 Ashmem::~Ashmem()
200 {
201 }
202
CreateAshmem(const char * name,int32_t size)203 sptr<Ashmem> Ashmem::CreateAshmem(const char *name, int32_t size)
204 {
205 if ((name == nullptr) || (size <= 0)) {
206 UTILS_LOGE("%{public}s: Parameter is invalid, size= %{public}d", __func__, size);
207 return nullptr;
208 }
209
210 int fd = AshmemCreate(name, size);
211 if (fd < 0) {
212 UTILS_LOGE("%{public}s: Failed to exec AshmemCreate, fd= %{public}d", __func__, size);
213 return nullptr;
214 }
215
216 return new Ashmem(fd, size);
217 }
218
CloseAshmem()219 void Ashmem::CloseAshmem()
220 {
221 if (memoryFd_ > 0) {
222 ::close(memoryFd_);
223 memoryFd_ = -1;
224 }
225 memorySize_ = 0;
226 flag_ = 0;
227 startAddr_ = nullptr;
228 }
229
MapAshmem(int mapType)230 bool Ashmem::MapAshmem(int mapType)
231 {
232 void *startAddr = ::mmap(nullptr, memorySize_, mapType, MAP_SHARED, memoryFd_, 0);
233 if (startAddr == MAP_FAILED) {
234 UTILS_LOGE("Failed to exec mmap");
235 return false;
236 }
237
238 startAddr_ = startAddr;
239 flag_ = mapType;
240
241 return true;
242 }
243
MapReadAndWriteAshmem()244 bool Ashmem::MapReadAndWriteAshmem()
245 {
246 return MapAshmem(PROT_READ | PROT_WRITE);
247 }
248
MapReadOnlyAshmem()249 bool Ashmem::MapReadOnlyAshmem()
250 {
251 return MapAshmem(PROT_READ);
252 }
253
UnmapAshmem()254 void Ashmem::UnmapAshmem()
255 {
256 if (startAddr_ != nullptr) {
257 ::munmap(startAddr_, memorySize_);
258 startAddr_ = nullptr;
259 }
260 flag_ = 0;
261 }
262
SetProtection(int protectionType)263 bool Ashmem::SetProtection(int protectionType)
264 {
265 int result = AshmemSetProt(memoryFd_, protectionType);
266 return result >= 0;
267 }
268
GetProtection()269 int Ashmem::GetProtection()
270 {
271 return TEMP_FAILURE_RETRY(ioctl(memoryFd_, ASHMEM_GET_PROT_MASK));
272 }
273
GetAshmemSize()274 int32_t Ashmem::GetAshmemSize()
275 {
276 return AshmemGetSize(memoryFd_);
277 }
278
WriteToAshmem(const void * data,int32_t size,int32_t offset)279 bool Ashmem::WriteToAshmem(const void *data, int32_t size, int32_t offset)
280 {
281 if (data == nullptr) {
282 return false;
283 }
284
285 if (!CheckValid(size, offset, PROT_WRITE)) {
286 UTILS_LOGE("%{public}s: invalid input or not map", __func__);
287 return false;
288 }
289
290 auto tmpData = reinterpret_cast<char *>(startAddr_);
291 int ret = memcpy_s(tmpData + offset, memorySize_ - offset, reinterpret_cast<const char *>(data), size);
292 if (ret != EOK) {
293 UTILS_LOGE("%{public}s: Failed to memcpy, ret = %{public}d", __func__, ret);
294 return false;
295 }
296
297 return true;
298 }
299
ReadFromAshmem(int32_t size,int32_t offset)300 const void *Ashmem::ReadFromAshmem(int32_t size, int32_t offset)
301 {
302 if (!CheckValid(size, offset, PROT_READ)) {
303 UTILS_LOGE("%{public}s: invalid input or not map", __func__);
304 return nullptr;
305 }
306
307 return reinterpret_cast<const char *>(startAddr_) + offset;
308 }
309
CheckValid(int32_t size,int32_t offset,int cmd)310 bool Ashmem::CheckValid(int32_t size, int32_t offset, int cmd)
311 {
312 if (startAddr_ == nullptr) {
313 return false;
314 }
315 if ((size < 0) || (size > memorySize_) || (offset < 0) || (offset > memorySize_)) {
316 UTILS_LOGE("%{public}s: , invalid parameter, size = %{public}d, memorySize_ = %{public}d, offset = %{public}d",
317 __func__, size, memorySize_, offset);
318 return false;
319 }
320 if (offset + size > memorySize_) {
321 UTILS_LOGE("%{public}s: , invalid parameter, size = %{public}d, memorySize_ = %{public}d, offset = %{public}d",
322 __func__, size, memorySize_, offset);
323 return false;
324 }
325 if (!(static_cast<uint32_t>(GetProtection()) & static_cast<uint32_t>(cmd)) ||
326 !(static_cast<uint32_t>(flag_) & static_cast<uint32_t>(cmd))) {
327 return false;
328 }
329
330 return true;
331 }
332 }
333