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