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