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