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 33struct 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 42static 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 54static 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 66static bool IsPurgMemPtrValid(struct PurgMem *purgObj); 67static bool IsPurged(struct PurgMem *purgObj); 68static int TypeCast(void); 69 70static 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 112deinit_upt: 113 DeinitUxPageTable(pugObj->uxPageTable); 114free_uxpt: 115 free(pugObj->uxPageTable); 116 pugObj->uxPageTable = NULL; 117unmap_data: 118 munmap(pugObj->dataPtr, size); 119 pugObj->dataPtr = NULL; 120free_pug_obj: 121 free(pugObj); 122 pugObj = NULL; 123 124 return NULL; 125} 126 127struct 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 153bool 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 212static 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 222static 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 238static 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 261static 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 289bool 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 323bool 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 368static 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 382void PurgMemEndRead(struct PurgMem *purgObj) 383{ 384 EndAccessPurgMem(purgObj); 385} 386 387void PurgMemEndWrite(struct PurgMem *purgObj) 388{ 389 EndAccessPurgMem(purgObj); 390} 391 392void *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 401size_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 410bool 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 429static 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 439static 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}