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