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 IpcIoPushInt32(request, shmId);
104 IpcIoPushInt32(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 = IpcIoPopInt32(request);
118 dataInfo->length = IpcIoPopInt32(request); // make sure all data are popped out.
119
120 if (shmId == -1) {
121 HILOGE("[AieIpc]shmId is invalid: %d.", shmId);
122 return RETCODE_FAILURE;
123 }
124 if (dataInfo->length <= 0) {
125 HILOGE("[AieIpc]dataInfo->length is invalid: %d.", dataInfo->length);
126 ReleaseShmId(shmId);
127 return RETCODE_FAILURE;
128 }
129
130 char *shared = reinterpret_cast<char *>(shmat(shmId, nullptr, 0));
131 if (shared == reinterpret_cast<char *>(-1)) {
132 HILOGE("[AieIpc]shmat failed %d.", errno);
133 ReleaseShmId(shmId);
134 return RETCODE_FAILURE;
135 }
136
137 if (shared == nullptr) {
138 HILOGE("[AieIpc]shared data is nullptr.");
139 ReleaseShmId(shmId);
140 return RETCODE_NULL_PARAM;
141 }
142
143 dataInfo->data = reinterpret_cast<unsigned char *>(malloc(dataInfo->length));
144 if (dataInfo->data == nullptr) {
145 shmdt(shared);
146 ReleaseShmId(shmId);
147 HILOGE("[AieIpc]Failed to malloc memory.");
148 return RETCODE_OUT_OF_MEMORY;
149 }
150 OHOS::AI::MallocPointerGuard<unsigned char> dataInfoGuard(dataInfo->data);
151
152 errno_t retCode = memcpy_s(dataInfo->data, dataInfo->length, shared, dataInfo->length);
153 if (retCode != EOK) {
154 shmdt(shared);
155 ReleaseShmId(shmId);
156 HILOGE("[AieIpc]Failed to memory copy, retCode[%d].", retCode);
157 return RETCODE_MEMORY_COPY_FAILURE;
158 }
159
160 if (shmdt(shared) == -1) {
161 ReleaseShmId(shmId);
162 HILOGE("[AieIpc]shmdt failed: %d.", errno);
163 return RETCODE_FAILURE;
164 }
165
166 ReleaseShmId(shmId);
167
168 dataInfoGuard.Detach();
169 return RETCODE_SUCCESS;
170 }
171
172 /**
173 * Use ipc to pop memory.
174 *
175 * @param [in] request Ipc handle.
176 * @param [out] dataInfo Data received.
177 * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
178 */
IpcIoPopMemory(IpcIo * request,DataInfo * dataInfo)179 int IpcIoPopMemory(IpcIo *request, DataInfo *dataInfo)
180 {
181 // internal call, no need to check null.
182 uint32_t dataBufSize = 0;
183 void *dataBuf = IpcIoPopFlatObj(request, &dataBufSize);
184 if (dataBuf == nullptr) {
185 HILOGE("[AieIpc]The UnParcel dataBuf is invalid.");
186 return RETCODE_NULL_PARAM;
187 }
188 if (static_cast<int>(dataBufSize) != dataInfo->length) {
189 HILOGE("[AieIpc]The UnParcel dataBufSize[%ud] doesn't match dataInfo->length[%d].",
190 dataBufSize, dataInfo->length);
191 return RETCODE_NULL_PARAM;
192 }
193
194 dataInfo->data = reinterpret_cast<unsigned char *>(malloc(dataBufSize));
195 if (dataInfo->data == nullptr) {
196 HILOGE("[AieIpc]Failed to malloc memory.");
197 return RETCODE_OUT_OF_MEMORY;
198 }
199 errno_t retCode = memcpy_s(dataInfo->data, dataInfo->length, dataBuf, dataBufSize);
200 if (retCode != EOK) {
201 HILOGE("[AieIpc]Failed to memory copy, retCode[%d].", retCode);
202 FreeDataInfo(dataInfo);
203 return RETCODE_MEMORY_COPY_FAILURE;
204 }
205 return RETCODE_SUCCESS;
206 }
207 } // anonymous namespace
208
ParcelDataInfo(IpcIo * request,const DataInfo * dataInfo,const uid_t receiverUid)209 void ParcelDataInfo(IpcIo *request, const DataInfo *dataInfo, const uid_t receiverUid)
210 {
211 if (dataInfo == nullptr) {
212 HILOGE("[AieIpc]The dataInfo is invalid.");
213 return;
214 }
215 if (request == nullptr) {
216 HILOGE("[AieIpc]The request is nullptr.");
217 return;
218 }
219 if (dataInfo->data != nullptr && dataInfo->length <= 0) { // invalid datainfo
220 HILOGE("[AieIpc]dataInfo->data != nullptr, dataInfo->length <= 0.");
221 return;
222 }
223 if (dataInfo->data == nullptr && dataInfo->length != 0) { // invalid datainfo
224 HILOGE("[AieIpc]dataInfo->data == nullptr, dataInfo->length != 0.");
225 return;
226 }
227
228 // parcel data length first.
229 IpcIoPushInt32(request, dataInfo->length);
230 if (dataInfo->data == nullptr && dataInfo->length == 0) { // empty datainfo, no need to parcel, save length(0) only.
231 return;
232 }
233 // parcel the data only if the data length > 0
234 if (dataInfo->length < IPC_MAX_TRANS_CAPACITY) {
235 IpcIoPushFlatObj(request, dataInfo->data, static_cast<uint32_t>(dataInfo->length));
236 } else {
237 IpcIoPushSharedMemory(request, dataInfo, receiverUid);
238 }
239 }
240
UnParcelDataInfo(IpcIo * request,DataInfo * dataInfo)241 int UnParcelDataInfo(IpcIo *request, DataInfo *dataInfo)
242 {
243 if (request == nullptr) {
244 HILOGE("[AieIpc]The request is nullptr.");
245 return RETCODE_FAILURE;
246 }
247 if (dataInfo == nullptr) {
248 HILOGE("[AieIpc]The dataInfo is nullptr.");
249 return RETCODE_FAILURE;
250 }
251
252 dataInfo->length = IpcIoPopInt32(request);
253 if (dataInfo->length < 0) {
254 HILOGE("[AieIpc]The dataInfo length is invalid.");
255 return RETCODE_FAILURE;
256 }
257 if (dataInfo->length == 0) { // no following buffer to unparcel.
258 dataInfo->data = nullptr;
259 return RETCODE_SUCCESS;
260 }
261
262 if (dataInfo->length < IPC_MAX_TRANS_CAPACITY) {
263 return IpcIoPopMemory(request, dataInfo);
264 } else {
265 return IpcIoPopSharedMemory(request, dataInfo);
266 }
267 }
268
FreeDataInfo(DataInfo * dataInfo)269 void FreeDataInfo(DataInfo *dataInfo)
270 {
271 if (dataInfo != nullptr && dataInfo->data != nullptr) {
272 free(dataInfo->data);
273 dataInfo->data = nullptr;
274 dataInfo->length = 0;
275 }
276 }
277