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     PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
157     LogPurgMemInfo(purgObj);
158 
159     PMState err = PM_OK;
160     /* destroy rwlock */
161     int ret = pthread_rwlock_destroy(&(purgObj->rwlock));
162     if (ret != 0) {
163         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: pthread_rwlock_destroy fail, %{public}d", __func__, ret);
164     }
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                 err = PM_UXPT_PRESENT_DATA_PURGED;
185             }
186             purgObj->dataPtr = NULL;
187         }
188     }
189     /* unmap uxpt */
190     if (purgObj->uxPageTable) {
191         PMState deinitRet = DeinitUxPageTable(purgObj->uxPageTable);
192         if (deinitRet != PM_OK) {
193             PM_HILOG_ERROR_C(LOG_CORE,
194                 "%{public}s: deinit upt fail, %{public}s", __func__, GetPMStateName(deinitRet));
195             err = deinitRet;
196         } else {
197             free(purgObj->uxPageTable);
198             purgObj->uxPageTable = NULL;
199         }
200     }
201 
202     if (err == PM_OK) {
203         free(purgObj);
204         purgObj = NULL; /* set input para NULL to avoid UAF */
205         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: succ", __func__);
206         return true;
207     }
208     PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: fail, %{public}s", __func__, GetPMStateName(err));
209     return false;
210 }
211 
IsPurgMemPtrValid(struct PurgMem * purgObj)212 static bool IsPurgMemPtrValid(struct PurgMem *purgObj)
213 {
214     IF_NULL_LOG_ACTION(purgObj, "obj is NULL", return false);
215     IF_NULL_LOG_ACTION(purgObj->dataPtr, "dataPtr is NULL", return false);
216     IF_NULL_LOG_ACTION(purgObj->uxPageTable, "pageTable is NULL", return false);
217     IF_NULL_LOG_ACTION(purgObj->builder, "builder is NULL", return false);
218 
219     return true;
220 }
221 
PurgMemBuildData(struct PurgMem * purgObj)222 static inline bool PurgMemBuildData(struct PurgMem *purgObj)
223 {
224     bool succ = false;
225     /* clear content before rebuild */
226     if (memset_s(purgObj->dataPtr, RoundUp(purgObj->dataSizeInput, PAGE_SIZE), 0, purgObj->dataSizeInput) != EOK) {
227         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s, clear content fail", __func__);
228         return succ;
229     }
230     /* @purgObj->builder is not NULL since it is checked by IsPurgMemPtrValid() before */
231     succ = PurgMemBuilderBuildAll(purgObj->builder, purgObj->dataPtr, purgObj->dataSizeInput);
232     if (succ) {
233         purgObj->buildDataCount++;
234     }
235     return succ;
236 }
237 
TryBeginRead(struct PurgMem * purgObj)238 static PMState TryBeginRead(struct PurgMem *purgObj)
239 {
240     int rwlockRet = pthread_rwlock_rdlock(&(purgObj->rwlock));
241     if (rwlockRet != 0) {
242         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: rdlock fail. %{public}d", __func__, rwlockRet);
243         return PM_LOCK_READ_FAIL;
244     }
245 
246     if (!IsPurged(purgObj)) {
247         PM_HILOG_INFO_C(LOG_CORE,
248             "%{public}s: not purged, return true. MAP_PUG=0x%{public}x", __func__, MAP_PURGEABLE);
249         return PM_DATA_NO_PURGED;
250     }
251 
252     rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
253     if (rwlockRet != 0) {
254         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: rd unlock fail. %{public}d", __func__, rwlockRet);
255         return PM_UNLOCK_READ_FAIL;
256     }
257 
258     return PM_DATA_PURGED;
259 }
260 
BeginReadBuildData(struct PurgMem * purgObj)261 static PMState BeginReadBuildData(struct PurgMem *purgObj)
262 {
263     bool rebuildRet = false;
264     int rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
265     if (rwlockRet) {
266         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
267         return PM_LOCK_WRITE_FAIL;
268     }
269 
270     if (IsPurged(purgObj)) {
271         rebuildRet = PurgMemBuildData(purgObj);
272         PM_HILOG_ERROR_C(LOG_CORE,
273             "%{public}s: purged, after built %{public}s", __func__, rebuildRet ? "succ" : "fail");
274     }
275 
276     rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
277     if (rwlockRet != 0) {
278         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
279         return PM_UNLOCK_WRITE_FAIL;
280     }
281 
282     if (!rebuildRet) {
283         return PMB_BUILD_ALL_FAIL;
284     }
285 
286     return PMB_BUILD_ALL_SUCC;
287 }
288 
PurgMemBeginRead(struct PurgMem * purgObj)289 bool PurgMemBeginRead(struct PurgMem *purgObj)
290 {
291     if (!IsPurgMemPtrValid(purgObj)) {
292         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
293         return false;
294     }
295     PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
296     LogPurgMemInfo(purgObj);
297     bool ret = false;
298     PMState err = PM_OK;
299     UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
300     while (true) {
301         err = TryBeginRead(purgObj);
302         if (err == PM_DATA_NO_PURGED) {
303             ret = true;
304             break;
305         } else if (err != PM_DATA_PURGED) {
306             break;
307         }
308 
309         err = BeginReadBuildData(purgObj);
310         if (err != PMB_BUILD_ALL_SUCC) {
311             ret = false;
312             break;
313         }
314     }
315 
316     if (!ret) {
317         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, UxptePut.", __func__, GetPMStateName(err));
318         UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
319     }
320     return ret;
321 }
322 
PurgMemBeginWrite(struct PurgMem * purgObj)323 bool PurgMemBeginWrite(struct PurgMem *purgObj)
324 {
325     if (!IsPurgMemPtrValid(purgObj)) {
326         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
327         return false;
328     }
329     PM_HILOG_INFO_C(LOG_CORE, "%{public}s: LogPurgMemInfo:", __func__);
330     LogPurgMemInfo(purgObj);
331     int rwlockRet = 0;
332     bool rebuildRet = false;
333     PMState err = PM_OK;
334 
335     UxpteGet(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
336 
337     rwlockRet = pthread_rwlock_wrlock(&(purgObj->rwlock));
338     if (rwlockRet != 0) {
339         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wrlock fail. %{public}d", __func__, rwlockRet);
340         err = PM_LOCK_WRITE_FAIL;
341         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, return false, UxptePut.", __func__, GetPMStateName(err));
342         UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
343         return false;
344     }
345 
346     if (!IsPurged(purgObj)) {
347         return true;
348     }
349 
350     /* data is purged */
351     rebuildRet = PurgMemBuildData(purgObj);
352     PM_HILOG_INFO_C(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, rebuildRet ? "succ" : "fail");
353     if (rebuildRet) {
354         return true;
355     }
356     /* data is purged and rebuild failed. return false */
357     err = PMB_BUILD_ALL_FAIL;
358     rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
359     if (rwlockRet != 0) {
360         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: wr unlock fail. %{public}d", __func__, rwlockRet);
361     }
362 
363     PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: %{public}s, return false, UxptePut.", __func__, GetPMStateName(err));
364     UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
365     return false;
366 }
367 
EndAccessPurgMem(struct PurgMem * purgObj)368 static inline void EndAccessPurgMem(struct PurgMem *purgObj)
369 {
370     if (!IsPurgMemPtrValid(purgObj)) {
371         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
372         return;
373     }
374     int rwlockRet = 0;
375     rwlockRet = pthread_rwlock_unlock(&(purgObj->rwlock));
376     if (rwlockRet != 0) {
377         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: unlock fail. %{public}d", __func__, rwlockRet);
378     }
379     UxptePut(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
380 }
381 
PurgMemEndRead(struct PurgMem * purgObj)382 void PurgMemEndRead(struct PurgMem *purgObj)
383 {
384     EndAccessPurgMem(purgObj);
385 }
386 
PurgMemEndWrite(struct PurgMem * purgObj)387 void PurgMemEndWrite(struct PurgMem *purgObj)
388 {
389     EndAccessPurgMem(purgObj);
390 }
391 
PurgMemGetContent(struct PurgMem * purgObj)392 void *PurgMemGetContent(struct PurgMem *purgObj)
393 {
394     if (!IsPurgMemPtrValid(purgObj)) {
395         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
396         return NULL;
397     }
398     return purgObj->dataPtr;
399 }
400 
PurgMemGetContentSize(struct PurgMem * purgObj)401 size_t PurgMemGetContentSize(struct PurgMem *purgObj)
402 {
403     if (!IsPurgMemPtrValid(purgObj)) {
404         PM_HILOG_ERROR_C(LOG_CORE, "%{public}s: para is invalid", __func__);
405         return 0;
406     }
407     return purgObj->dataSizeInput;
408 }
409 
PurgMemAppendModify(struct PurgMem * purgObj,PurgMemModifyFunc func,void * funcPara)410 bool PurgMemAppendModify(struct PurgMem *purgObj, PurgMemModifyFunc func, void *funcPara)
411 {
412     IF_NULL_LOG_ACTION(func, "input func is NULL", return true);
413     IF_NULL_LOG_ACTION(purgObj, "input purgObj is NULL", return false);
414     /* apply modify */
415     bool succ = func(purgObj->dataPtr, purgObj->dataSizeInput, funcPara);
416     if (!succ) {
417         return false;
418     }
419     struct PurgMemBuilder *builder = PurgMemBuilderCreate(func, funcPara, NULL);
420     IF_NULL_LOG_ACTION(builder, "PurgMemBuilderCreate fail", return false);
421 
422     if (purgObj->builder == NULL) { /* PurgMemObj has no builder previous */
423         purgObj->builder = builder;
424         return true;
425     }
426     return PurgMemBuilderAppendBuilder(purgObj->builder, builder);
427 }
428 
IsPurged(struct PurgMem * purgObj)429 static bool IsPurged(struct PurgMem *purgObj)
430 {
431     /* first access, return true means purged */
432     if (purgObj->buildDataCount == 0) {
433         PM_HILOG_INFO_C(LOG_CORE, "%{public}s, has never built, return true", __func__);
434         return true;
435     }
436     return !UxpteIsPresent(purgObj->uxPageTable, (uint64_t)(purgObj->dataPtr), purgObj->dataSizeInput);
437 }
438 
TypeCast(void)439 static int TypeCast(void)
440 {
441     unsigned int utype = MAP_ANONYMOUS;
442     utype |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE);
443     int type = (int) utype;
444     return type;
445 }