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 #ifdef UTILS_CXX_RUST
CreateAshmemStd(const char * name,int32_t size)39 std::shared_ptr<Ashmem> CreateAshmemStd(const char *name, int32_t size)
40 {
41     if ((name == nullptr) || (size <= 0)) {
42         UTILS_LOGE("%{public}s: Parameter is invalid, size= %{public}d", __func__, size);
43         return std::shared_ptr<Ashmem>{};
44     }
45 
46     int fd = AshmemCreate(name, size);
47     if (fd < 0) {
48         UTILS_LOGE("%{public}s: Failed to exec AshmemCreate, fd= %{public}d", __func__, size);
49         return std::shared_ptr<Ashmem>{};
50     }
51 
52     return std::make_shared<Ashmem>(fd, size);
53 }
54 
AsVoidPtr(const char * inPtr)55 const c_void* AsVoidPtr(const char* inPtr)
56 {
57     return static_cast<const c_void*>(inPtr);
58 }
59 
AsCharPtr(const c_void * inPtr)60 const char* AsCharPtr(const c_void* inPtr)
61 {
62     return static_cast<const char*>(inPtr);
63 }
64 #endif
65 
AshmemOpenLocked()66 static int AshmemOpenLocked()
67 {
68     int fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
69     if (fd < 0) {
70         UTILS_LOGE("%{public}s: fd is invalid, fd = %{public}d, errno = %{public}d", __func__, fd, errno);
71         return fd;
72     }
73 
74     struct stat st;
75     int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
76     if (ret < 0) {
77         UTILS_LOGE("%{public}s: Failed to exec fstat, ret = %{public}d, errno = %{public}d", __func__, ret, errno);
78         close(fd);
79         return ret;
80     }
81 
82     if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
83         UTILS_LOGE("%{public}s: stat status is invalid, st_mode = %{public}u", __func__, st.st_mode);
84         close(fd);
85         return -1;
86     }
87     return fd;
88 }
89 
AshmemOpen()90 static int AshmemOpen()
91 {
92     pthread_mutex_lock(&g_ashmemLock);
93     int fd = AshmemOpenLocked();
94     pthread_mutex_unlock(&g_ashmemLock);
95     return fd;
96 }
97 
98 /*
99  * AshmemCreate - create a new ashmem region and returns the file descriptor
100  * fd < 0 means failed
101  *
102  */
AshmemCreate(const char * name,size_t size)103 int AshmemCreate(const char *name, size_t size)
104 {
105     int ret;
106     int fd = AshmemOpen();
107     if (fd < 0) {
108         UTILS_LOGE("%{public}s: Failed to exec AshmemOpen fd = %{public}d", __func__, fd);
109         return fd;
110     }
111 
112     if (name != nullptr) {
113         char buf[ASHMEM_NAME_LEN] = {0};
114         ret = strcpy_s(buf, sizeof(buf), name);
115         if (ret != EOK) {
116             UTILS_LOGE("%{public}s: Failed to exec strcpy_s, name= %{public}s, ret= %{public}d", __func__, name, ret);
117             close(fd);
118             return -1;
119         }
120         ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
121         if (ret < 0) {
122             UTILS_LOGE("%{public}s: Failed to set name, name= %{public}s, ret= %{public}d, errno = %{public}d",
123                        __func__, name, ret,  errno);
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("%{public}s: Failed to set size, size= %{public}zu, errno = %{public}d", __func__, size, errno);
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,int32_t size)148 Ashmem::Ashmem(int fd, int32_t size) : memoryFd_(fd), memorySize_(size), flag_(0), startAddr_(nullptr)
149 {
150 }
151 
~Ashmem()152 Ashmem::~Ashmem()
153 {
154     UnmapAshmem();
155     CloseAshmem();
156 }
157 
CreateAshmem(const char * name,int32_t size)158 sptr<Ashmem> Ashmem::CreateAshmem(const char *name, int32_t size)
159 {
160     if ((name == nullptr) || (size <= 0)) {
161         UTILS_LOGE("%{public}s: Parameter is invalid, size= %{public}d", __func__, size);
162         return nullptr;
163     }
164 
165     int fd = AshmemCreate(name, size);
166     if (fd < 0) {
167         UTILS_LOGE("%{public}s: Failed to exec AshmemCreate, fd= %{public}d", __func__, size);
168         return nullptr;
169     }
170 
171     return new Ashmem(fd, size);
172 }
173 
SetProtection(int protectionType) const174 bool Ashmem::SetProtection(int protectionType) const
175 {
176     int result = AshmemSetProt(memoryFd_, protectionType);
177     return result >= 0;
178 }
179 
GetProtection() const180 int Ashmem::GetProtection() const
181 {
182     return TEMP_FAILURE_RETRY(ioctl(memoryFd_, ASHMEM_GET_PROT_MASK));
183 }
184 
GetAshmemSize() const185 int32_t Ashmem::GetAshmemSize() const
186 {
187     return AshmemGetSize(memoryFd_);
188 }
189 
190 #ifdef UTILS_CXX_RUST
CloseAshmem() const191 void Ashmem::CloseAshmem() const
192 #else
193 void Ashmem::CloseAshmem()
194 #endif
195 {
196     if (memoryFd_ > 0) {
197         ::close(memoryFd_);
198         memoryFd_ = -1;
199     }
200     memorySize_ = 0;
201     flag_ = 0;
202     startAddr_ = nullptr;
203 }
204 
205 #ifdef UTILS_CXX_RUST
MapAshmem(int mapType) const206 bool Ashmem::MapAshmem(int mapType) const
207 #else
208 bool Ashmem::MapAshmem(int mapType)
209 #endif
210 {
211     void *startAddr = ::mmap(nullptr, memorySize_, mapType, MAP_SHARED, memoryFd_, 0);
212     if (startAddr == MAP_FAILED) {
213         UTILS_LOGE("Failed to exec mmap, errno = %{public}d", errno);
214         return false;
215     }
216 
217     startAddr_ = startAddr;
218     flag_ = mapType;
219 
220     return true;
221 }
222 
223 #ifdef UTILS_CXX_RUST
MapReadAndWriteAshmem() const224 bool Ashmem::MapReadAndWriteAshmem() const
225 #else
226 bool Ashmem::MapReadAndWriteAshmem()
227 #endif
228 {
229     return MapAshmem(PROT_READ | PROT_WRITE);
230 }
231 
232 #ifdef UTILS_CXX_RUST
MapReadOnlyAshmem() const233 bool Ashmem::MapReadOnlyAshmem() const
234 #else
235 bool Ashmem::MapReadOnlyAshmem()
236 #endif
237 {
238     return MapAshmem(PROT_READ);
239 }
240 
241 #ifdef UTILS_CXX_RUST
UnmapAshmem() const242 void Ashmem::UnmapAshmem() const
243 #else
244 void Ashmem::UnmapAshmem()
245 #endif
246 {
247     if (startAddr_ != nullptr) {
248         ::munmap(startAddr_, memorySize_);
249         startAddr_ = nullptr;
250     }
251     flag_ = 0;
252 }
253 
254 #ifdef UTILS_CXX_RUST
WriteToAshmem(const void * data,int32_t size,int32_t offset) const255 bool Ashmem::WriteToAshmem(const void *data, int32_t size, int32_t offset) const
256 #else
257 bool Ashmem::WriteToAshmem(const void *data, int32_t size, int32_t offset)
258 #endif
259 {
260     if (data == nullptr) {
261         return false;
262     }
263 
264     if (!CheckValid(size, offset, PROT_WRITE)) {
265         UTILS_LOGE("%{public}s: invalid input or not map", __func__);
266         return false;
267     }
268 
269     auto tmpData = reinterpret_cast<char *>(startAddr_);
270     int ret = memcpy_s(tmpData + offset, memorySize_ - offset, reinterpret_cast<const char *>(data), size);
271     if (ret != EOK) {
272         UTILS_LOGE("%{public}s: Failed to memcpy, ret = %{public}d", __func__, ret);
273         return false;
274     }
275 
276     return true;
277 }
278 
279 #ifdef UTILS_CXX_RUST
ReadFromAshmem(int32_t size,int32_t offset) const280 const void *Ashmem::ReadFromAshmem(int32_t size, int32_t offset) const
281 #else
282 const void *Ashmem::ReadFromAshmem(int32_t size, int32_t offset)
283 #endif
284 {
285     if (!CheckValid(size, offset, PROT_READ)) {
286         UTILS_LOGE("%{public}s: invalid input or not map", __func__);
287         return nullptr;
288     }
289 
290     return reinterpret_cast<const char *>(startAddr_) + offset;
291 }
292 
CheckValid(int32_t size,int32_t offset,int cmd) const293 bool Ashmem::CheckValid(int32_t size, int32_t offset, int cmd) const
294 {
295     if (startAddr_ == nullptr) {
296         return false;
297     }
298     if ((size < 0) || (size > memorySize_) || (offset < 0) || (offset > memorySize_)) {
299         UTILS_LOGE("%{public}s: , invalid parameter, size = %{public}d, memorySize_ = %{public}d, offset = %{public}d",
300             __func__, size, memorySize_, offset);
301         return false;
302     }
303     if (offset + size > memorySize_) {
304         UTILS_LOGE("%{public}s: , invalid parameter, size = %{public}d, memorySize_ = %{public}d, offset = %{public}d",
305             __func__, size, memorySize_, offset);
306         return false;
307     }
308     if (!(static_cast<uint32_t>(GetProtection()) & static_cast<uint32_t>(cmd)) ||
309         !(static_cast<uint32_t>(flag_) & static_cast<uint32_t>(cmd))) {
310         return false;
311     }
312 
313     return true;
314 }
315 }
316