• 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 (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 }