1 /*
2 * Copyright (c) 2022 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 <stdlib.h> /* malloc */
17 #include <sys/mman.h> /* mmap */
18 #include <pthread.h>
19 #include <stdio.h> /* FILE */
20
21 #include "securec.h"
22 #include "../../common/include/pm_ptr_util.h"
23 #include "../../common/include/pm_util.h"
24 #include "../../common/include/pm_state_c.h"
25 #include "../../common/include/ux_page_table_c.h"
26 #include "purgeable_mem_builder_c.h"
27 #include "purgeable_mem_c.h"
28
29 #undef LOG_TAG
30 #define LOG_TAG "PurgeableMemC"
31
32 struct PurgMem {
33 void *dataPtr;
34 size_t dataSizeInput;
35 struct PurgMemBuilder *builder;
36 UxPageTableStruct *uxPageTable;
37 pthread_rwlock_t rwlock;
38 unsigned int buildDataCount;
39 };
40
LogPurgMemInfo_(struct PurgMem * obj)41 static inline void LogPurgMemInfo_(struct PurgMem *obj)
42 {
43 HILOG_INFO(LOG_CORE, "purgMemObj(%{public}lx) dataPtr(%{public}lx) dataSizeInput(%{public}zu)"
44 " builderPtr(%{public}lx) uxpt(%{public}lx)",
45 (unsigned long)obj, (unsigned long)(obj->dataPtr), obj->dataSizeInput,
46 (unsigned long)(obj->builder), (unsigned long)(obj->uxPageTable));
47 }
48
RoundUp_(size_t val,size_t align)49 static inline size_t RoundUp_(size_t val, size_t align)
50 {
51 if (align == 0) {
52 return val;
53 }
54 return ((val + align - 1) / align) * align;
55 }
56
57 static bool IsPurgMemPtrValid_(struct PurgMem *purgObj);
58 static bool IsPurged_(struct PurgMem *purgObj);
59
PurgMemCreate_(size_t len,struct PurgMemBuilder * builder)60 static struct PurgMem *PurgMemCreate_(size_t len, struct PurgMemBuilder *builder)
61 {
62 /* PurgMemObj allow no builder temporaily */
63 struct PurgMem *pugObj = NULL;
64 pugObj = (struct PurgMem *)malloc(sizeof(struct PurgMem));
65 if (!pugObj) {
66 HILOG_ERROR(LOG_CORE, "%{public}s: malloc struct PurgMem fail", __func__);
67 return NULL;
68 }
69 size_t size = RoundUp_(len, PAGE_SIZE);
70 int type = MAP_ANONYMOUS;
71 type |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE);
72 pugObj->dataPtr = mmap(NULL, size, PROT_READ | PROT_WRITE, type, -1, 0);
73 if (pugObj->dataPtr == MAP_FAILED) {
74 HILOG_ERROR(LOG_CORE, "%{public}s: mmap dataPtr fail", __func__);
75 pugObj->dataPtr = NULL;
76 goto free_pug_obj;
77 }
78
79 pugObj->uxPageTable = (UxPageTableStruct *)malloc(UxPageTableSize());
80 if (!(pugObj->uxPageTable)) {
81 HILOG_ERROR(LOG_CORE, "%{public}s: malloc UxPageTableStruct fail", __func__);
82 goto unmap_data;
83 }
84 PMState err = InitUxPageTable(pugObj->uxPageTable, (uint64_t)(pugObj->dataPtr), size); /* dataPtr is aligned */
85 if (err != PM_OK) {
86 HILOG_ERROR(LOG_CORE, "%{public}s: InitUxPageTable fail, %{public}s", __func__, GetPMStateName(err));
87 goto free_uxpt;
88 }
89 int lockInitRet = pthread_rwlock_init(&(pugObj->rwlock), NULL);
90 if (lockInitRet != 0) {
91 HILOG_ERROR(LOG_CORE, "%{public}s: pthread_rwlock_init fail, %{public}d", __func__, lockInitRet);
92 goto deinit_upt;
93 }
94 pugObj->builder = builder;
95 pugObj->dataSizeInput = len;
96 pugObj->buildDataCount = 0;
97
98 HILOG_INFO(LOG_CORE, "%{public}s: LogPurgMemInfo_:", __func__);
99 LogPurgMemInfo_(pugObj);
100 return pugObj;
101
102 deinit_upt:
103 DeinitUxPageTable(pugObj->uxPageTable);
104 free_uxpt:
105 free(pugObj->uxPageTable);
106 pugObj->uxPageTable = NULL;
107 unmap_data:
108 munmap(pugObj->dataPtr, size);
109 pugObj->dataPtr = NULL;
110 free_pug_obj:
111 free(pugObj);
112 pugObj = NULL;
113
114 return NULL;
115 }
116
PurgMemCreate(size_t len,PurgMemModifyFunc func,void * funcPara)117 struct PurgMem *PurgMemCreate(size_t len, PurgMemModifyFunc func, void *funcPara)
118 {
119 if (len == 0) {
120 HILOG_ERROR(LOG_CORE, "%{public}s: input len 0", __func__);
121 return NULL;
122 }
123 /* a PurgMemObj must have builder */
124 IF_NULL_LOG_ACTION(func, "%{public}s: input func is NULL", return NULL);
125 struct PurgMem *purgMemObj = PurgMemCreate_(len, NULL);
126 /* create fail */
127 if (!purgMemObj) {
128 return purgMemObj;
129 }
130
131 if (PurgMemAppendModify(purgMemObj, func, funcPara)) {
132 return purgMemObj;
133 }
134
135 /* append func fail meas create builder failed */
136 HILOG_ERROR(LOG_CORE, "%{public}s: append mod func fail", __func__);
137 if (!PurgMemDestroy(purgMemObj)) {
138 HILOG_ERROR(LOG_CORE, "%{public}s: destroy PurgMem fail after append modFunc fail", __func__);
139 }
140 return NULL;
141 }
142
PurgMemDestroy(struct PurgMem * purgObj)143 bool PurgMemDestroy(struct PurgMem *purgObj)
144 {
145 IF_NULL_LOG_ACTION(purgObj, "input is NULL", return true);
146 HILOG_INFO(LOG_CORE, "%{public}s: LogPurgMemInfo_:", __func__);
147 LogPurgMemInfo_(purgObj);
148
149 PMState err = PM_OK;
150 /* destroy rwlock */
151 int ret = pthread_rwlock_destroy(&(purgObj->rwlock));
152 if (ret != 0) {
153 HILOG_ERROR(LOG_CORE, "%{public}s: pthread_rwlock_destroy fail, %{public}d", __func__, ret);
154 }
155 /* destroy builder */
156 if (purgObj->builder) {
157 if (!PurgMemBuilderDestroy(purgObj->builder)) {
158 HILOG_ERROR(LOG_CORE, "%{public}s: PurgMemBuilderDestroy fail", __func__);
159 err = PMB_DESTORY_FAIL;
160 } else {
161 purgObj->builder = NULL;
162 }
163 }
164 /* unmap purgeable mem region */
165 if (purgObj->dataPtr) {
166 size_t size = RoundUp_(purgObj->dataSizeInput, PAGE_SIZE);
167 if (munmap(purgObj->dataPtr, size) != 0) {
168 HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__);
169 err = PM_UNMAP_PURG_FAIL;
170 } else {
171 /* double check munmap result: if uxpte is set to no_present */
172 if (UxpteIsEnabled() && !IsPurged_(purgObj)) {
173 HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__);
174 err = PM_UXPT_PRESENT_DATA_PURGED;
175 }
176 purgObj->dataPtr = NULL;
177 }
178 }
179 /* unmap uxpt */
180 if (purgObj->uxPageTable) {
181 PMState deinitRet = DeinitUxPageTable(purgObj->uxPageTable);
182 if (deinitRet != PM_OK) {
183 HILOG_ERROR(LOG_CORE, "%{public}s: deinit upt fail, %{public}s", __func__, GetPMStateName(deinitRet));
184 err = deinitRet;
185 } else {
186 free(purgObj->uxPageTable);
187 purgObj->uxPageTable = NULL;
188 }
189 }
190
191 if (err == PM_OK) {
192 free(purgObj);
193 purgObj = NULL; /* set input para NULL to avoid UAF */
194 HILOG_ERROR(LOG_CORE, "%{public}s: succ", __func__);
195 return true;
196 }
197 HILOG_ERROR(LOG_CORE, "%{public}s: fail, %{public}s", __func__, GetPMStateName(err));
198 return false;
199 }
200
IsPurgMemPtrValid_(struct PurgMem * purgObj)201 static bool IsPurgMemPtrValid_(struct PurgMem *purgObj)
202 {
203 IF_NULL_LOG_ACTION(purgObj, "obj is NULL", return false);
204 IF_NULL_LOG_ACTION(purgObj->dataPtr, "dataPtr is NULL", return false);
205 IF_NULL_LOG_ACTION(purgObj->uxPageTable, "pageTable is NULL", return false);
206 IF_NULL_LOG_ACTION(purgObj->builder, "builder is NULL", return false);
207
208 return true;
209 }
210
PurgMemBuildData_(struct PurgMem * purgObj)211 static inline bool PurgMemBuildData_(struct PurgMem *purgObj)
212 {
213 bool succ = false;
214 /* clear content before rebuild */
215 if (memset_s(purgObj->dataPtr, RoundUp_(purgObj->dataSizeInput, PAGE_SIZE), 0, purgObj->dataSizeInput) != EOK) {
216 HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__);
217 return succ;
218 }
219 /* @purgObj->builder is not NULL since it is checked by IsPurgMemPtrValid_() before */
220 succ = PurgMemBuilderBuildAll(purgObj->builder, purgObj->dataPtr, purgObj->dataSizeInput);
221 if (succ) {
222 purgObj->buildDataCount++;
223 }
224 return succ;
225 }
226
TryBeginRead_(struct PurgMem * purgObj)227 static PMState TryBeginRead_(struct PurgMem *purgObj)
228 {
229 int rwlockRet = pthread_rwlock_rdlock(&(purgObj->rwlock));
230 if (rwlockRet != 0) {
231 HILOG_ERROR(LOG_CORE, "%{public}s: rdlock fail. %{public}d", __func__, rwlockRet);
232 return PM_LOCK_READ_FAIL;
233 }
234
235 if (!IsPurged_(purgObj)) {
236 HILOG_INFO(LOG_CORE, "%{public}s: not purged, return true. MAP_PUG=0x%{public}x", __func__, MAP_PURGEABLE);
237 return PM_DATA_NO_PURGED;
238 }
239
240 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
241 if (rwlockRet != 0) {
242 HILOG_ERROR(LOG_CORE, "%{public}s: rd unlock fail. %{public}d", __func__, rwlockRet);
243 return PM_UNLOCK_READ_FAIL;
244 }
245
246 return PM_DATA_PURGED;
247 }
248
BeginReadBuildData_(struct PurgMem * purgObj)249 static PMState BeginReadBuildData_(struct PurgMem *purgObj)
250 {
251 bool rebuildRet = false;
252 int rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
253 if (rwlockRet) {
254 HILOG_ERROR(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
255 return PM_LOCK_WRITE_FAIL;
256 }
257
258 if (IsPurged_(purgObj)) {
259 rebuildRet = PurgMemBuildData_(purgObj);
260 HILOG_ERROR(LOG_CORE, "%{public}s: purged, after built %{public}s", __func__, rebuildRet ? "succ" : "fail");
261 }
262
263 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
264 if (rwlockRet != 0) {
265 HILOG_ERROR(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
266 return PM_UNLOCK_WRITE_FAIL;
267 }
268
269 if (!rebuildRet) {
270 return PMB_BUILD_ALL_FAIL;
271 }
272
273 return PMB_BUILD_ALL_SUCC;
274 }
275
PurgMemBeginRead(struct PurgMem * purgObj)276 bool PurgMemBeginRead(struct PurgMem *purgObj)
277 {
278 if (!IsPurgMemPtrValid_(purgObj)) {
279 HILOG_ERROR(LOG_CORE, "%{public}s: para is invalid", __func__);
280 return false;
281 }
282 HILOG_INFO(LOG_CORE, "%{public}s: LogPurgMemInfo_:", __func__);
283 LogPurgMemInfo_(purgObj);
284 bool ret = false;
285 PMState err = PM_OK;
286 UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
287 while (true) {
288 err = TryBeginRead_(purgObj);
289 if (err == PM_DATA_NO_PURGED) {
290 ret = true;
291 break;
292 } else if (err != PM_DATA_PURGED) {
293 break;
294 }
295
296 err = BeginReadBuildData_(purgObj);
297 if (err != PMB_BUILD_ALL_SUCC) {
298 ret = false;
299 break;
300 }
301 }
302
303 if (!ret) {
304 HILOG_ERROR(LOG_CORE, "%{public}s: %{public}s, UxptePut.", __func__, GetPMStateName(err));
305 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
306 }
307 return ret;
308 }
309
PurgMemBeginWrite(struct PurgMem * purgObj)310 bool PurgMemBeginWrite(struct PurgMem *purgObj)
311 {
312 if (!IsPurgMemPtrValid_(purgObj)) {
313 HILOG_ERROR(LOG_CORE, "%{public}s: para is invalid", __func__);
314 return false;
315 }
316 HILOG_INFO(LOG_CORE, "%{public}s: LogPurgMemInfo_:", __func__);
317 LogPurgMemInfo_(purgObj);
318 int rwlockRet = 0;
319 bool rebuildRet = false;
320 PMState err = PM_OK;
321
322 UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
323
324 rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
325 if (rwlockRet != 0) {
326 HILOG_ERROR(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
327 err = PM_LOCK_WRITE_FAIL;
328 goto uxpte_put;
329 }
330
331 if (!IsPurged_(purgObj)) {
332 goto succ;
333 }
334
335 /* data is purged */
336 rebuildRet = PurgMemBuildData_(purgObj);
337 HILOG_INFO(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, rebuildRet ? "succ" : "fail");
338 if (rebuildRet) {
339 goto succ;
340 }
341 /* data is purged and rebuild failed. return false */
342 err = PMB_BUILD_ALL_FAIL;
343 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
344 if (rwlockRet != 0) {
345 HILOG_ERROR(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
346 }
347
348 uxpte_put:
349 HILOG_ERROR(LOG_CORE, "%{public}s: %{public}s, return false, UxptePut.", __func__, GetPMStateName(err));
350 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
351 return false;
352 succ:
353 return true;
354 }
355
EndAccessPurgMem_(struct PurgMem * purgObj)356 static inline void EndAccessPurgMem_(struct PurgMem *purgObj)
357 {
358 if (!IsPurgMemPtrValid_(purgObj)) {
359 HILOG_ERROR(LOG_CORE, "%{public}s: para is invalid", __func__);
360 return;
361 }
362 int rwlockRet = 0;
363 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
364 if (rwlockRet != 0) {
365 HILOG_ERROR(LOG_CORE, "%{public}s: unlock fail. %{public}d", __func__, rwlockRet);
366 }
367 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
368 }
369
PurgMemEndRead(struct PurgMem * purgObj)370 void PurgMemEndRead(struct PurgMem *purgObj)
371 {
372 EndAccessPurgMem_(purgObj);
373 }
374
PurgMemEndWrite(struct PurgMem * purgObj)375 void PurgMemEndWrite(struct PurgMem *purgObj)
376 {
377 EndAccessPurgMem_(purgObj);
378 }
379
PurgMemGetContent(struct PurgMem * purgObj)380 void *PurgMemGetContent(struct PurgMem *purgObj)
381 {
382 if (!IsPurgMemPtrValid_(purgObj)) {
383 HILOG_ERROR(LOG_CORE, "%{public}s: para is invalid", __func__);
384 return NULL;
385 }
386 return purgObj->dataPtr;
387 }
388
PurgMemGetContentSize(struct PurgMem * purgObj)389 size_t PurgMemGetContentSize(struct PurgMem *purgObj)
390 {
391 if (!IsPurgMemPtrValid_(purgObj)) {
392 HILOG_ERROR(LOG_CORE, "%{public}s: para is invalid", __func__);
393 return 0;
394 }
395 return purgObj->dataSizeInput;
396 }
397
PurgMemAppendModify(struct PurgMem * purgObj,PurgMemModifyFunc func,void * funcPara)398 bool PurgMemAppendModify(struct PurgMem *purgObj, PurgMemModifyFunc func, void *funcPara)
399 {
400 IF_NULL_LOG_ACTION(func, "input func is NULL", return true);
401 IF_NULL_LOG_ACTION(purgObj, "input purgObj is NULL", return false);
402 /* apply modify */
403 bool succ = func(purgObj->dataPtr, purgObj->dataSizeInput, funcPara);
404 if (!succ) {
405 return false;
406 }
407 struct PurgMemBuilder *builder = PurgMemBuilderCreate(func, funcPara, NULL);
408 IF_NULL_LOG_ACTION(builder, "PurgMemBuilderCreate fail", return false);
409
410 if (purgObj->builder == NULL) { /* PurgMemObj has no builder previous */
411 purgObj->builder = builder;
412 return true;
413 }
414 return PurgMemBuilderAppendBuilder(purgObj->builder, builder);
415 }
416
IsPurged_(struct PurgMem * purgObj)417 static bool IsPurged_(struct PurgMem *purgObj)
418 {
419 /* first access, return true means purged */
420 if (purgObj->buildDataCount == 0) {
421 HILOG_INFO(LOG_CORE, "%{public}s, has never built, return true", __func__);
422 return true;
423 }
424 return !UxpteIsPresent(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
425 }
426