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