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 "platform/os_wrapper/ipc/include/aie_ipc.h"
17
18 #include <cstdlib>
19 #include <sys/shm.h>
20
21 #include "securec.h"
22
23 #include "protocol/retcode_inner/aie_retcode_inner.h"
24 #include "utils/aie_guard.h"
25 #include "utils/log/aie_log.h"
26
27 namespace {
28 constexpr int IPC_MAX_TRANS_CAPACITY = 200; // memory beyond this limit will use shared memory
29 constexpr int SHM_KEY_START = 200000; // chosen randomly
30 constexpr int SHM_KEY_END = 300000; // chosen randomly
31 constexpr unsigned int SHM_READ_WRITE_PERMISSIONS = 0777U;
32
ReleaseShmId(const int shmId)33 void ReleaseShmId(const int shmId)
34 {
35 if (shmId == -1) {
36 return;
37 }
38 if (shmctl(shmId, IPC_RMID, nullptr) == -1) {
39 HILOGE("[AieIpc]shmctl IPC_RMID failed: %d.", errno);
40 return;
41 }
42 }
43
44 /**
45 * Use shared memory to push large memory.
46 *
47 * @param [in] request Ipc handle.
48 * @param [in] dataInfo Data need to transfer.
49 * @param receiverUid receiver's uid.
50 */
IpcIoPushSharedMemory(IpcIo * request,const DataInfo * dataInfo,const uid_t receiverUid)51 void IpcIoPushSharedMemory(IpcIo *request, const DataInfo *dataInfo, const uid_t receiverUid)
52 {
53 // internal call, no need to check null.
54 static int shmKey = SHM_KEY_START;
55 int shmId;
56 while ((shmId = shmget(shmKey, dataInfo->length, SHM_READ_WRITE_PERMISSIONS | IPC_CREAT | IPC_EXCL)) < 0) {
57 if (errno == EEXIST) {
58 ++shmKey;
59 if (shmKey >= SHM_KEY_END) {
60 shmKey = SHM_KEY_START;
61 }
62 continue;
63 }
64 HILOGE("[AieIpc]shmget failed: %d.", errno);
65 return;
66 }
67 HILOGI("[AieIpc]shmget succeed, shmKey = %d, shmId = %d.", shmKey, shmId);
68
69 char *shared = reinterpret_cast<char *>(shmat(shmId, nullptr, 0));
70 if (shared == reinterpret_cast<char *>(-1)) {
71 ReleaseShmId(shmId);
72 HILOGE("[AieIpc]shmat failed: %d.", errno);
73 return;
74 }
75
76 int retCode;
77 if ((retCode = memcpy_s(shared, dataInfo->length, dataInfo->data, dataInfo->length)) != EOK) {
78 shmdt(shared);
79 ReleaseShmId(shmId);
80 HILOGE("[AieIpc]memcpy_s failed: %d.", retCode);
81 return;
82 }
83
84 if (shmdt(shared) == -1) {
85 ReleaseShmId(shmId);
86 HILOGE("[AieIpc]shmdt failed: %d.", errno);
87 return;
88 }
89
90 struct shmid_ds shmidDs {};
91 if (shmctl(shmId, IPC_STAT, &shmidDs) == -1) {
92 HILOGE("[AieIpc]shmctl IPC_STAT failed: %d.", errno);
93 ReleaseShmId(shmId);
94 }
95
96 shmidDs.shm_perm.uid = receiverUid; // give receiver the privilege to release shared memory.
97 if (shmctl(shmId, IPC_SET, &shmidDs) == -1) {
98 ReleaseShmId(shmId);
99 HILOGE("[AieIpc]shmctl IPC_SET failed: %d.", errno);
100 return;
101 }
102
103 WriteInt32(request, shmId);
104 WriteInt32(request, dataInfo->length);
105 }
106
107 /**
108 * Use shared memory to pop large memory.
109 *
110 * @param [in] request Ipc handle.
111 * @param [out] dataInfo Data received.
112 * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
113 */
IpcIoPopSharedMemory(IpcIo * request,DataInfo * dataInfo)114 int IpcIoPopSharedMemory(IpcIo *request, DataInfo *dataInfo)
115 {
116 // internal call, no need to check null.
117 int shmId;
118 ReadInt32(request, &shmId);
119 ReadInt32(request, &(dataInfo->length)); // make sure all data are popped out.
120
121 if (shmId == -1) {
122 HILOGE("[AieIpc]shmId is invalid: %d.", shmId);
123 return RETCODE_FAILURE;
124 }
125 if (dataInfo->length <= 0) {
126 HILOGE("[AieIpc]dataInfo->length is invalid: %d.", dataInfo->length);
127 ReleaseShmId(shmId);
128 return RETCODE_FAILURE;
129 }
130
131 char *shared = reinterpret_cast<char *>(shmat(shmId, nullptr, 0));
132 if (shared == reinterpret_cast<char *>(-1)) {
133 HILOGE("[AieIpc]shmat failed %d.", errno);
134 ReleaseShmId(shmId);
135 return RETCODE_FAILURE;
136 }
137
138 if (shared == nullptr) {
139 HILOGE("[AieIpc]shared data is nullptr.");
140 ReleaseShmId(shmId);
141 return RETCODE_NULL_PARAM;
142 }
143
144 dataInfo->data = reinterpret_cast<unsigned char *>(malloc(dataInfo->length));
145 if (dataInfo->data == nullptr) {
146 shmdt(shared);
147 ReleaseShmId(shmId);
148 HILOGE("[AieIpc]Failed to malloc memory.");
149 return RETCODE_OUT_OF_MEMORY;
150 }
151 OHOS::AI::MallocPointerGuard<unsigned char> dataInfoGuard(dataInfo->data);
152
153 errno_t retCode = memcpy_s(dataInfo->data, dataInfo->length, shared, dataInfo->length);
154 if (retCode != EOK) {
155 shmdt(shared);
156 ReleaseShmId(shmId);
157 HILOGE("[AieIpc]Failed to memory copy, retCode[%d].", retCode);
158 return RETCODE_MEMORY_COPY_FAILURE;
159 }
160
161 if (shmdt(shared) == -1) {
162 ReleaseShmId(shmId);
163 HILOGE("[AieIpc]shmdt failed: %d.", errno);
164 return RETCODE_FAILURE;
165 }
166
167 ReleaseShmId(shmId);
168
169 dataInfoGuard.Detach();
170 return RETCODE_SUCCESS;
171 }
172
173 /**
174 * Use ipc to pop memory.
175 *
176 * @param [in] request Ipc handle.
177 * @param [out] dataInfo Data received.
178 * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
179 */
IpcIoPopMemory(IpcIo * request,DataInfo * dataInfo)180 int IpcIoPopMemory(IpcIo *request, DataInfo *dataInfo)
181 {
182 // internal call, no need to check null.
183 uint32_t dataBufSize = 0;
184 ReadUint32(request, &dataBufSize);
185 void *dataBuf = (void *)ReadBuffer(request, dataBufSize);
186 if (dataBuf == nullptr) {
187 HILOGE("[AieIpc]The UnParcel dataBuf is invalid.");
188 return RETCODE_NULL_PARAM;
189 }
190 if (static_cast<int>(dataBufSize) != dataInfo->length) {
191 HILOGE("[AieIpc]The UnParcel dataBufSize[%ud] doesn't match dataInfo->length[%d].",
192 dataBufSize, dataInfo->length);
193 return RETCODE_NULL_PARAM;
194 }
195
196 dataInfo->data = reinterpret_cast<unsigned char *>(malloc(dataBufSize));
197 if (dataInfo->data == nullptr) {
198 HILOGE("[AieIpc]Failed to malloc memory.");
199 return RETCODE_OUT_OF_MEMORY;
200 }
201 errno_t retCode = memcpy_s(dataInfo->data, dataInfo->length, dataBuf, dataBufSize);
202 if (retCode != EOK) {
203 HILOGE("[AieIpc]Failed to memory copy, retCode[%d].", retCode);
204 FreeDataInfo(dataInfo);
205 return RETCODE_MEMORY_COPY_FAILURE;
206 }
207 return RETCODE_SUCCESS;
208 }
209 } // anonymous namespace
210
ParcelDataInfo(IpcIo * request,const DataInfo * dataInfo,const uid_t receiverUid)211 void ParcelDataInfo(IpcIo *request, const DataInfo *dataInfo, const uid_t receiverUid)
212 {
213 if (dataInfo == nullptr) {
214 HILOGE("[AieIpc]The dataInfo is invalid.");
215 return;
216 }
217 if (request == nullptr) {
218 HILOGE("[AieIpc]The request is nullptr.");
219 return;
220 }
221 if (dataInfo->data != nullptr && dataInfo->length <= 0) { // invalid datainfo
222 HILOGE("[AieIpc]dataInfo->data != nullptr, dataInfo->length <= 0.");
223 return;
224 }
225 if (dataInfo->data == nullptr && dataInfo->length != 0) { // invalid datainfo
226 HILOGE("[AieIpc]dataInfo->data == nullptr, dataInfo->length != 0.");
227 return;
228 }
229
230 // parcel data length first.
231 WriteInt32(request, dataInfo->length);
232 if (dataInfo->data == nullptr && dataInfo->length == 0) { // empty datainfo, no need to parcel, save length(0) only.
233 return;
234 }
235 // parcel the data only if the data length > 0
236 if (dataInfo->length < IPC_MAX_TRANS_CAPACITY) {
237 WriteUint32(request, static_cast<uint32_t>(dataInfo->length));
238 WriteBuffer(request, dataInfo->data, static_cast<uint32_t>(dataInfo->length));
239 } else {
240 IpcIoPushSharedMemory(request, dataInfo, receiverUid);
241 }
242 }
243
UnParcelDataInfo(IpcIo * request,DataInfo * dataInfo)244 int UnParcelDataInfo(IpcIo *request, DataInfo *dataInfo)
245 {
246 if (request == nullptr) {
247 HILOGE("[AieIpc]The request is nullptr.");
248 return RETCODE_FAILURE;
249 }
250 if (dataInfo == nullptr) {
251 HILOGE("[AieIpc]The dataInfo is nullptr.");
252 return RETCODE_FAILURE;
253 }
254
255 ReadInt32(request, &(dataInfo->length));
256 if (dataInfo->length < 0) {
257 HILOGE("[AieIpc]The dataInfo length is invalid.");
258 return RETCODE_FAILURE;
259 }
260 if (dataInfo->length == 0) { // no following buffer to unparcel.
261 dataInfo->data = nullptr;
262 return RETCODE_SUCCESS;
263 }
264
265 if (dataInfo->length < IPC_MAX_TRANS_CAPACITY) {
266 return IpcIoPopMemory(request, dataInfo);
267 } else {
268 return IpcIoPopSharedMemory(request, dataInfo);
269 }
270 }
271
FreeDataInfo(DataInfo * dataInfo)272 void FreeDataInfo(DataInfo *dataInfo)
273 {
274 if (dataInfo != nullptr && dataInfo->data != nullptr) {
275 free(dataInfo->data);
276 dataInfo->data = nullptr;
277 dataInfo->length = 0;
278 }
279 }
280