xref: /commonlibrary/c_utils/base/src/ashmem.cpp (revision 3f4cbf05)
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
35namespace OHOS {
36static pthread_mutex_t g_ashmemLock = PTHREAD_MUTEX_INITIALIZER;
37
38#ifdef UTILS_CXX_RUST
39std::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
55const c_void* AsVoidPtr(const char* inPtr)
56{
57    return static_cast<const c_void*>(inPtr);
58}
59
60const char* AsCharPtr(const c_void* inPtr)
61{
62    return static_cast<const char*>(inPtr);
63}
64#endif
65
66static 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
90static 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 */
103int 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
138int AshmemSetProt(int fd, int prot)
139{
140    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
141}
142
143int AshmemGetSize(int fd)
144{
145    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
146}
147
148Ashmem::Ashmem(int fd, int32_t size) : memoryFd_(fd), memorySize_(size), flag_(0), startAddr_(nullptr)
149{
150}
151
152Ashmem::~Ashmem()
153{
154    UnmapAshmem();
155    CloseAshmem();
156}
157
158sptr<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
174bool Ashmem::SetProtection(int protectionType) const
175{
176    int result = AshmemSetProt(memoryFd_, protectionType);
177    return result >= 0;
178}
179
180int Ashmem::GetProtection() const
181{
182    return TEMP_FAILURE_RETRY(ioctl(memoryFd_, ASHMEM_GET_PROT_MASK));
183}
184
185int32_t Ashmem::GetAshmemSize() const
186{
187    return AshmemGetSize(memoryFd_);
188}
189
190#ifdef UTILS_CXX_RUST
191void Ashmem::CloseAshmem() const
192#else
193void 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
206bool Ashmem::MapAshmem(int mapType) const
207#else
208bool 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
224bool Ashmem::MapReadAndWriteAshmem() const
225#else
226bool Ashmem::MapReadAndWriteAshmem()
227#endif
228{
229    return MapAshmem(PROT_READ | PROT_WRITE);
230}
231
232#ifdef UTILS_CXX_RUST
233bool Ashmem::MapReadOnlyAshmem() const
234#else
235bool Ashmem::MapReadOnlyAshmem()
236#endif
237{
238    return MapAshmem(PROT_READ);
239}
240
241#ifdef UTILS_CXX_RUST
242void Ashmem::UnmapAshmem() const
243#else
244void 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
255bool Ashmem::WriteToAshmem(const void *data, int32_t size, int32_t offset) const
256#else
257bool 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
280const void *Ashmem::ReadFromAshmem(int32_t size, int32_t offset) const
281#else
282const 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
293bool 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