• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }