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