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 UTILS_LOGE("Failed to open libashmemd_client.so");
46 return nullptr;
47 }
48 openFdFunction func = reinterpret_cast<openFdFunction>(dlsym(handle, "openAshmemdFd"));
49 if (!func) {
50 dlclose(handle);
51 UTILS_LOGE("Failed to obtain address of openFdFunction");
52 }
53 return func;
54 }
55
AshmemOpenLocked()56 static int AshmemOpenLocked()
57 {
58 int fd = -1;
59
60 if (g_openFdApi == nullptr) {
61 g_openFdApi = ProbeAshmemFdFunction();
62 }
63
64 if (g_openFdApi != nullptr) {
65 fd = g_openFdApi();
66 } else {
67 fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
68 }
69
70 if (fd < 0) {
71 UTILS_LOGE("fd is invalid");
72 return fd;
73 }
74
75 struct stat st;
76 int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
77 if (ret < 0) {
78 UTILS_LOGE("Failed to exec fstat");
79 close(fd);
80 return ret;
81 }
82
83 if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
84 UTILS_LOGE("stat status is invalid");
85 close(fd);
86 return -1;
87 }
88 return fd;
89 }
90
AshmemOpen()91 static int AshmemOpen()
92 {
93 pthread_mutex_lock(&g_ashmemLock);
94 int fd = AshmemOpenLocked();
95 pthread_mutex_unlock(&g_ashmemLock);
96 return fd;
97 }
98
99 /*
100 * AshmemCreate - create a new ashmem region and returns the file descriptor
101 * fd < 0 means failed
102 *
103 */
AshmemCreate(const char * name,size_t size)104 int AshmemCreate(const char *name, size_t size)
105 {
106 int ret;
107 int fd = AshmemOpen();
108 if (fd < 0) {
109 UTILS_LOGE("Failed to exec AshmemOpen");
110 return fd;
111 }
112
113 if (name != nullptr) {
114 char buf[ASHMEM_NAME_LEN] = {0};
115 ret = strcpy_s(buf, sizeof(buf), name);
116 if (ret != EOK) {
117 UTILS_LOGE("Failed to exec strcpy_s");
118 close(fd);
119 return -1;
120 }
121 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
122 if (ret < 0) {
123 UTILS_LOGE("Failed to exec ioctl");
124 close(fd);
125 return ret;
126 }
127 }
128
129 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
130 if (ret < 0) {
131 UTILS_LOGE("Failed to exec ioctl");
132 close(fd);
133 return ret;
134 }
135 return fd;
136 }
137
AshmemSetProt(int fd,int prot)138 int AshmemSetProt(int fd, int prot)
139 {
140 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
141 }
142
AshmemGetSize(int fd)143 int AshmemGetSize(int fd)
144 {
145 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
146 }
147
Ashmem(int fd,int size)148 Ashmem::Ashmem(int fd, int size) : memoryFd_(fd), memorySize_(size), flag_(0), startAddr_(nullptr)
149 {
150 }
151
~Ashmem()152 Ashmem::~Ashmem()
153 {
154 }
155
CreateAshmem(const char * name,int32_t size)156 sptr<Ashmem> Ashmem::CreateAshmem(const char *name, int32_t size)
157 {
158 if ((name == nullptr) || (size <= 0)) {
159 UTILS_LOGE("Parameter is invalid");
160 return nullptr;
161 }
162
163 int fd = AshmemCreate(name, size);
164 if (fd < 0) {
165 UTILS_LOGE("Failed to exec AshmemCreate");
166 return nullptr;
167 }
168
169 return new Ashmem(fd, size);
170 }
171
CloseAshmem()172 void Ashmem::CloseAshmem()
173 {
174 if (memoryFd_ > 0) {
175 ::close(memoryFd_);
176 memoryFd_ = -1;
177 }
178 memorySize_ = 0;
179 flag_ = 0;
180 startAddr_ = nullptr;
181 }
182
MapAshmem(int mapType)183 bool Ashmem::MapAshmem(int mapType)
184 {
185 void *startAddr = ::mmap(nullptr, memorySize_, mapType, MAP_SHARED, memoryFd_, 0);
186 if (startAddr == MAP_FAILED) {
187 UTILS_LOGE("Failed to exec mmap");
188 return false;
189 }
190
191 startAddr_ = startAddr;
192 flag_ = mapType;
193
194 return true;
195 }
196
MapReadAndWriteAshmem()197 bool Ashmem::MapReadAndWriteAshmem()
198 {
199 return MapAshmem(PROT_READ | PROT_WRITE);
200 }
201
MapReadOnlyAshmem()202 bool Ashmem::MapReadOnlyAshmem()
203 {
204 return MapAshmem(PROT_READ);
205 }
206
UnmapAshmem()207 void Ashmem::UnmapAshmem()
208 {
209 if (startAddr_ != nullptr) {
210 ::munmap(startAddr_, memorySize_);
211 startAddr_ = nullptr;
212 }
213 flag_ = 0;
214 }
215
SetProtection(int protectionType)216 bool Ashmem::SetProtection(int protectionType)
217 {
218 int result = AshmemSetProt(memoryFd_, protectionType);
219 return result >= 0;
220 }
221
GetProtection()222 int Ashmem::GetProtection()
223 {
224 return TEMP_FAILURE_RETRY(ioctl(memoryFd_, ASHMEM_GET_PROT_MASK));
225 }
226
GetAshmemSize()227 int32_t Ashmem::GetAshmemSize()
228 {
229 return AshmemGetSize(memoryFd_);
230 }
231
WriteToAshmem(const void * data,int32_t size,int32_t offset)232 bool Ashmem::WriteToAshmem(const void *data, int32_t size, int32_t offset)
233 {
234 if (data == nullptr) {
235 return false;
236 }
237
238 if (!CheckValid(size, offset, PROT_WRITE)) {
239 UTILS_LOGE("%{public}s: invalid input or not map", __func__);
240 return false;
241 }
242
243 auto tmpData = reinterpret_cast<char *>(startAddr_);
244 int ret = memcpy_s(tmpData + offset, memorySize_ - offset, reinterpret_cast<const char *>(data), size);
245 if (ret != EOK) {
246 UTILS_LOGE("%{public}s: Failed to memcpy, ret = %{public}d", __func__, ret);
247 return false;
248 }
249
250 return true;
251 }
252
ReadFromAshmem(int32_t size,int32_t offset)253 const void *Ashmem::ReadFromAshmem(int32_t size, int32_t offset)
254 {
255 if (!CheckValid(size, offset, PROT_READ)) {
256 UTILS_LOGE("%{public}s: invalid input or not map", __func__);
257 return nullptr;
258 }
259
260 return reinterpret_cast<const char *>(startAddr_) + offset;
261 }
262
CheckValid(int32_t size,int32_t offset,int cmd)263 bool Ashmem::CheckValid(int32_t size, int32_t offset, int cmd)
264 {
265 if (startAddr_ == nullptr) {
266 return false;
267 }
268 if ((size < 0) || (size > memorySize_) || (offset < 0) || (offset > memorySize_)) {
269 return false;
270 }
271 if (offset + size > memorySize_) {
272 return false;
273 }
274 if (!(static_cast<uint32_t>(GetProtection()) & static_cast<uint32_t>(cmd)) ||
275 !(static_cast<uint32_t>(flag_) & static_cast<uint32_t>(cmd))) {
276 return false;
277 }
278
279 return true;
280 }
281 }
282