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 int rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
157 if (rwlockRet) {
158 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
159 return false;
160 }
161 PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
162 LogPurgMemInfo(purgObj);
163
164 PMState err = PM_OK;
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 }
185 purgObj->dataPtr = NULL;
186 }
187 }
188 /* unmap uxpt */
189 if (purgObj->uxPageTable) {
190 PMState deinitRet = DeinitUxPageTable(purgObj->uxPageTable);
191 if (deinitRet != PM_OK) {
192 PM_HILOG_ERROR_C(LOG_CORE,
193 "%{public}s: deinit upt fail, %{public}s", __func__, GetPMStateName(deinitRet));
194 err = deinitRet;
195 } else {
196 free(purgObj->uxPageTable);
197 purgObj->uxPageTable = NULL;
198 }
199 }
200 pthread_rwlock_unlock(&(purgObj->rwlock));
201
202 if (err == PM_OK) {
203 /* destroy rwlock */
204 rwlockRet = pthread_rwlock_destroy(&(purgObj->rwlock));
205 if (rwlockRet != 0) {
206 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: pthread_rwlock_destroy fail, %{public}d", __func__, rwlockRet);
207 }
208 free(purgObj);
209 purgObj = NULL; /* set input para NULL to avoid UAF */
210 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: succ", __func__);
211 return true;
212 }
213 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: fail, %{public}s", __func__, GetPMStateName(err));
214 return false;
215 }
216
IsPurgMemPtrValid(struct PurgMem * purgObj)217 static bool IsPurgMemPtrValid(struct PurgMem *purgObj)
218 {
219 IF_NULL_LOG_ACTION(purgObj, "obj is NULL", return false);
220 IF_NULL_LOG_ACTION(purgObj->dataPtr, "dataPtr is NULL", return false);
221 IF_NULL_LOG_ACTION(purgObj->uxPageTable, "pageTable is NULL", return false);
222 IF_NULL_LOG_ACTION(purgObj->builder, "builder is NULL", return false);
223
224 return true;
225 }
226
PurgMemBuildData(struct PurgMem * purgObj)227 static inline bool PurgMemBuildData(struct PurgMem *purgObj)
228 {
229 bool succ = false;
230 /* clear content before rebuild */
231 if (memset_s(purgObj->dataPtr, RoundUp(purgObj->dataSizeInput, PAGE_SIZE), 0, purgObj->dataSizeInput) != EOK) {
232 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s, clear content fail", __func__);
233 return succ;
234 }
235 /* @purgObj->builder is not NULL since it is checked by IsPurgMemPtrValid() before */
236 succ = PurgMemBuilderBuildAll(purgObj->builder, purgObj->dataPtr, purgObj->dataSizeInput);
237 if (succ) {
238 purgObj->buildDataCount++;
239 }
240 return succ;
241 }
242
TryBeginRead(struct PurgMem * purgObj)243 static PMState TryBeginRead(struct PurgMem *purgObj)
244 {
245 int rwlockRet = pthread_rwlock_rdlock(&(purgObj->rwlock));
246 if (rwlockRet != 0) {
247 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: rdlock fail. %{public}d", __func__, rwlockRet);
248 return PM_LOCK_READ_FAIL;
249 }
250
251 if (!IsPurged(purgObj)) {
252 PM_HILOG_INFO_C(LOG_CORE,
253 "%{public}s: not purged, return true. MAP_PUG=0x%{public}x", __func__, MAP_PURGEABLE);
254 return PM_DATA_NO_PURGED;
255 }
256
257 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
258 if (rwlockRet != 0) {
259 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: rd unlock fail. %{public}d", __func__, rwlockRet);
260 return PM_UNLOCK_READ_FAIL;
261 }
262
263 return PM_DATA_PURGED;
264 }
265
BeginReadBuildData(struct PurgMem * purgObj)266 static PMState BeginReadBuildData(struct PurgMem *purgObj)
267 {
268 bool rebuildRet = false;
269 int rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
270 if (rwlockRet) {
271 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
272 return PM_LOCK_WRITE_FAIL;
273 }
274
275 if (IsPurged(purgObj)) {
276 rebuildRet = PurgMemBuildData(purgObj);
277 PM_HILOG_ERROR_C(LOG_CORE,
278 "%{public}s: purged, after built %{public}s", __func__, rebuildRet ? "succ" : "fail");
279 }
280
281 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
282 if (rwlockRet != 0) {
283 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
284 return PM_UNLOCK_WRITE_FAIL;
285 }
286
287 if (!rebuildRet) {
288 return PMB_BUILD_ALL_FAIL;
289 }
290
291 return PMB_BUILD_ALL_SUCC;
292 }
293
PurgMemBeginRead(struct PurgMem * purgObj)294 bool PurgMemBeginRead(struct PurgMem *purgObj)
295 {
296 if (!IsPurgMemPtrValid(purgObj)) {
297 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
298 return false;
299 }
300 PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
301 LogPurgMemInfo(purgObj);
302 bool ret = false;
303 PMState err = PM_OK;
304 UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
305 while (true) {
306 err = TryBeginRead(purgObj);
307 if (err == PM_DATA_NO_PURGED) {
308 ret = true;
309 break;
310 } else if (err != PM_DATA_PURGED) {
311 break;
312 }
313
314 err = BeginReadBuildData(purgObj);
315 if (err != PMB_BUILD_ALL_SUCC) {
316 ret = false;
317 break;
318 }
319 }
320
321 if (!ret) {
322 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, UxptePut.", __func__, GetPMStateName(err));
323 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
324 }
325 return ret;
326 }
327
PurgMemBeginWrite(struct PurgMem * purgObj)328 bool PurgMemBeginWrite(struct PurgMem *purgObj)
329 {
330 if (!IsPurgMemPtrValid(purgObj)) {
331 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
332 return false;
333 }
334 PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
335 LogPurgMemInfo(purgObj);
336 int rwlockRet = 0;
337 bool rebuildRet = false;
338 PMState err = PM_OK;
339
340 UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
341
342 rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
343 if (rwlockRet != 0) {
344 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
345 err = PM_LOCK_WRITE_FAIL;
346 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, return false, UxptePut.", __func__, GetPMStateName(err));
347 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
348 return false;
349 }
350
351 if (!IsPurged(purgObj)) {
352 return true;
353 }
354
355 /* data is purged */
356 rebuildRet = PurgMemBuildData(purgObj);
357 PM_HILOG_INFO_C(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, rebuildRet ? "succ" : "fail");
358 if (rebuildRet) {
359 return true;
360 }
361 /* data is purged and rebuild failed. return false */
362 err = PMB_BUILD_ALL_FAIL;
363 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
364 if (rwlockRet != 0) {
365 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
366 }
367
368 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, return false, UxptePut.", __func__, GetPMStateName(err));
369 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
370 return false;
371 }
372
EndAccessPurgMem(struct PurgMem * purgObj)373 static inline void EndAccessPurgMem(struct PurgMem *purgObj)
374 {
375 if (!IsPurgMemPtrValid(purgObj)) {
376 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
377 return;
378 }
379 int rwlockRet = 0;
380 rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
381 if (rwlockRet != 0) {
382 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: unlock fail. %{public}d", __func__, rwlockRet);
383 }
384 UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
385 }
386
PurgMemEndRead(struct PurgMem * purgObj)387 void PurgMemEndRead(struct PurgMem *purgObj)
388 {
389 EndAccessPurgMem(purgObj);
390 }
391
PurgMemEndWrite(struct PurgMem * purgObj)392 void PurgMemEndWrite(struct PurgMem *purgObj)
393 {
394 EndAccessPurgMem(purgObj);
395 }
396
PurgMemGetContent(struct PurgMem * purgObj)397 void *PurgMemGetContent(struct PurgMem *purgObj)
398 {
399 if (!IsPurgMemPtrValid(purgObj)) {
400 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
401 return NULL;
402 }
403 return purgObj->dataPtr;
404 }
405
PurgMemGetContentSize(struct PurgMem * purgObj)406 size_t PurgMemGetContentSize(struct PurgMem *purgObj)
407 {
408 if (!IsPurgMemPtrValid(purgObj)) {
409 PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
410 return 0;
411 }
412 return purgObj->dataSizeInput;
413 }
414
PurgMemAppendModify(struct PurgMem * purgObj,PurgMemModifyFunc func,void * funcPara)415 bool PurgMemAppendModify(struct PurgMem *purgObj, PurgMemModifyFunc func, void *funcPara)
416 {
417 IF_NULL_LOG_ACTION(func, "input func is NULL", return true);
418 IF_NULL_LOG_ACTION(purgObj, "input purgObj is NULL", return false);
419 /* apply modify */
420 bool succ = func(purgObj->dataPtr, purgObj->dataSizeInput, funcPara);
421 if (!succ) {
422 return false;
423 }
424 struct PurgMemBuilder *builder = PurgMemBuilderCreate(func, funcPara, NULL);
425 IF_NULL_LOG_ACTION(builder, "PurgMemBuilderCreate fail", return false);
426
427 if (purgObj->builder == NULL) { /* PurgMemObj has no builder previous */
428 purgObj->builder = builder;
429 return true;
430 }
431 return PurgMemBuilderAppendBuilder(purgObj->builder, builder);
432 }
433
IsPurged(struct PurgMem * purgObj)434 static bool IsPurged(struct PurgMem *purgObj)
435 {
436 /* first access, return true means purged */
437 if (purgObj->buildDataCount == 0) {
438 PM_HILOG_INFO_C(LOG_CORE, "%{public}s, has never built, return true", __func__);
439 return true;
440 }
441 return !UxpteIsPresent(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
442 }
443
TypeCast(void)444 static int TypeCast(void)
445 {
446 unsigned int utype = MAP_ANONYMOUS;
447 utype |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE);
448 int type = (int) utype;
449 return type;
450 }