/* * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "los_load_elf.h" #include "fs/fd_table.h" #include "los_config.h" #include "los_vm_map.h" #include "los_vm_syscall.h" #include "los_vm_phys.h" #include "los_vm_dump.h" #ifdef LOSCFG_KERNEL_VDSO #include "los_vdso.h" #endif #ifdef LOSCFG_DRIVERS_TZDRIVER #include "tzdriver.h" #endif STATIC INT32 OsGetFileLength(UINT32 *fileLen, const CHAR *fileName) { struct stat buf; INT32 ret; ret = stat(fileName, &buf); if (ret < 0) { #ifndef LOSCFG_SHELL if (strcmp(fileName, "/bin/shell") != 0) { #endif PRINT_ERR("%s[%d], Failed to stat file: %s, errno: %d\n", __FUNCTION__, __LINE__, fileName, errno); #ifndef LOSCFG_SHELL } #endif return LOS_NOK; } if (S_ISREG(buf.st_mode) == 0) { PRINT_ERR("%s[%d], The file: %s is invalid!\n", __FUNCTION__, __LINE__, fileName); return LOS_NOK; } if (buf.st_size > FILE_LENGTH_MAX) { PRINT_ERR("%s[%d], The file: %s length is out of limit!\n", __FUNCTION__, __LINE__, fileName); return LOS_NOK; } *fileLen = (UINT32)buf.st_size; return LOS_OK; } STATIC INT32 OsReadELFInfo(INT32 fd, UINT8 *buffer, size_t readSize, off_t offset) { ssize_t byteNum; off_t returnPos; if (readSize > 0) { returnPos = lseek(fd, offset, SEEK_SET); if (returnPos != offset) { PRINT_ERR("%s[%d], Failed to seek the position!, offset: %#x\n", __FUNCTION__, __LINE__, offset); return LOS_NOK; } byteNum = read(fd, buffer, readSize); if (byteNum <= 0) { PRINT_ERR("%s[%d], Failed to read from offset: %#x!\n", __FUNCTION__, __LINE__, offset); return LOS_NOK; } } return LOS_OK; } STATIC INT32 OsVerifyELFEhdr(const LD_ELF_EHDR *ehdr, UINT32 fileLen) { if (memcmp(ehdr->elfIdent, LD_ELFMAG, LD_SELFMAG) != 0) { PRINT_ERR("%s[%d], The file is not elf!\n", __FUNCTION__, __LINE__); return LOS_NOK; } if ((ehdr->elfType != LD_ET_EXEC) && (ehdr->elfType != LD_ET_DYN)) { PRINT_ERR("%s[%d], The type of file is not ET_EXEC or ET_DYN!\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (ehdr->elfMachine != LD_EM_ARM) { PRINT_ERR("%s[%d], The type of machine is not EM_ARM!\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (ehdr->elfPhNum > ELF_PHDR_NUM_MAX) { PRINT_ERR("%s[%d], The num of program header is out of limit\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (ehdr->elfPhoff > fileLen) { PRINT_ERR("%s[%d], The offset of program header is invalid, elf file is bad\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (OsIsBadUserAddress((VADDR_T)ehdr->elfEntry)) { PRINT_ERR("%s[%d], The entry of program is invalid\n", __FUNCTION__, __LINE__); return LOS_NOK; } return LOS_OK; } STATIC INT32 OsVerifyELFPhdr(const LD_ELF_PHDR *phdr) { if ((phdr->fileSize > FILE_LENGTH_MAX) || (phdr->offset > FILE_LENGTH_MAX)) { PRINT_ERR("%s[%d], The size of phdr is out of limit\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (phdr->memSize > MEM_SIZE_MAX) { PRINT_ERR("%s[%d], The mem size of phdr is out of limit\n", __FUNCTION__, __LINE__); return LOS_NOK; } if (OsIsBadUserAddress((VADDR_T)phdr->vAddr)) { PRINT_ERR("%s[%d], The vaddr of phdr is invalid\n", __FUNCTION__, __LINE__); return LOS_NOK; } return LOS_OK; } STATIC INT32 OsELFLoadInit(const CHAR *fileName, ELFLoadInfo *loadInfo) { INT32 ret; #ifdef LOSCFG_FS_VFS const struct files_struct *oldFiles = OsCurrProcessGet()->files; loadInfo->oldFiles = (UINTPTR)create_files_snapshot(oldFiles); #else loadInfo->oldFiles = NULL; #endif loadInfo->execFD = INVALID_FD; loadInfo->interpFD = INVALID_FD; ret = OsGetFileLength(&loadInfo->execFileLen, fileName); if (ret != LOS_OK) { return -ENOENT; } loadInfo->execFD = open(fileName, O_RDONLY | O_EXECVE); if (loadInfo->execFD < 0) { if (get_errno() == EACCES) { return -EACCES; } PRINT_ERR("%s[%d], Failed to open ELF file: %s!\n", __FUNCTION__, __LINE__, fileName); return -ENOENT; } #ifdef LOSCFG_DRIVERS_TZDRIVER ret = fs_getfilep(loadInfo->execFD, &OsCurrProcessGet()->execFile); if (ret) { PRINT_ERR("%s[%d], Failed to get struct file %s!\n", __FUNCTION__, __LINE__, fileName); } #endif ret = OsReadELFInfo(loadInfo->execFD, (UINT8 *)&loadInfo->elfEhdr, sizeof(LD_ELF_EHDR), 0); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EIO; } ret = OsVerifyELFEhdr(&loadInfo->elfEhdr, loadInfo->execFileLen); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -ENOEXEC; } return LOS_OK; } STATIC INT32 OsLoadProgramHdrs(ELFLoadInfo *loadInfo) { LD_ELF_EHDR *elfEhdr = NULL; UINT32 size; INT32 ret; if (loadInfo->elfEhdr.elfPhNum < 1) { PRINT_ERR("%s[%d], No program sections in file: %s!\n", __FUNCTION__, __LINE__, loadInfo->fileName); return -ENOEXEC; } elfEhdr = &loadInfo->elfEhdr; if (elfEhdr->elfPhEntSize != sizeof(LD_ELF_PHDR)) { PRINT_ERR("%s[%d], e_phentsize is invalid, file: %s\n", __FUNCTION__, __LINE__, loadInfo->fileName); return -ENOEXEC; } size = sizeof(LD_ELF_PHDR) * elfEhdr->elfPhNum; if ((elfEhdr->elfPhoff + size) > loadInfo->execFileLen) { PRINT_ERR("%s[%d], Size for program header exceeds limit! file: %s\n", __FUNCTION__, __LINE__, loadInfo->fileName); return -ENOEXEC; } loadInfo->elfPhdr = LOS_MemAlloc(m_aucSysMem0, size); if (loadInfo->elfPhdr == NULL) { PRINT_ERR("%s[%d], Failed to allocate for elfPhdr! file: %s\n", __FUNCTION__, __LINE__, loadInfo->fileName); return -ENOMEM; } ret = OsReadELFInfo(loadInfo->execFD, (UINT8 *)loadInfo->elfPhdr, size, elfEhdr->elfPhoff); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->elfPhdr); loadInfo->elfPhdr = NULL; PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EIO; } return LOS_OK; } STATIC INT32 OsGetInterpEhdr(const CHAR *fileName, ELFLoadInfo *loadInfo) { INT32 ret; ret = OsGetFileLength(&loadInfo->interpFileLen, fileName); if (ret != LOS_OK) { return -ENOENT; } loadInfo->interpFD = open(fileName, O_RDONLY); if (loadInfo->interpFD < 0) { PRINT_ERR("%s[%d], Failed to open ELF file: %s!\n", __FUNCTION__, __LINE__, fileName); return -ENOENT; } ret = OsReadELFInfo(loadInfo->interpFD, (UINT8 *)&loadInfo->interpELFEhdr, sizeof(LD_ELF_EHDR), 0); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EIO; } ret = OsVerifyELFEhdr(&loadInfo->interpELFEhdr, loadInfo->interpFileLen); if (ret != LOS_OK) { PRINT_ERR("%s[%d], ELF header of file: %s is invalid!\n", __FUNCTION__, __LINE__, fileName); return -ELIBBAD; } return LOS_OK; } STATIC INT32 OsLoadInterpProgramHdrs(ELFLoadInfo *loadInfo) { LD_ELF_EHDR *elfEhdr = &loadInfo->interpELFEhdr; UINT32 size; INT32 ret; if (elfEhdr->elfPhNum < 1) { PRINT_ERR("%s[%d], No program sections of interpreter!\n", __FUNCTION__, __LINE__); return -ELIBBAD; } if (elfEhdr->elfPhEntSize != sizeof(LD_ELF_PHDR)) { PRINT_ERR("%s[%d], e_phentsize of interpreter is invalid!\n", __FUNCTION__, __LINE__); return -ELIBBAD; } size = sizeof(LD_ELF_PHDR) * elfEhdr->elfPhNum; if ((elfEhdr->elfPhoff + size) > loadInfo->interpFileLen) { PRINT_ERR("%s[%d], Size for program header of interpreter exceeds limit!\n", __FUNCTION__, __LINE__); return -ELIBBAD; } loadInfo->interpELFPhdr = LOS_MemAlloc(m_aucSysMem0, size); if (loadInfo->interpELFPhdr == NULL) { PRINT_ERR("%s[%d], Failed to allocate for elfPhdr of interpreter!\n", __FUNCTION__, __LINE__); return -ENOMEM; } ret = OsReadELFInfo(loadInfo->interpFD, (UINT8 *)loadInfo->interpELFPhdr, size, elfEhdr->elfPhoff); if (ret != LOS_OK) { (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->interpELFPhdr); loadInfo->interpELFPhdr = NULL; PRINT_ERR("%s[%d], Failed to read program header of interpreter!\n", __FUNCTION__, __LINE__); return -EIO; } return LOS_OK; } STATIC INT32 OsReadInterpInfo(ELFLoadInfo *loadInfo) { LD_ELF_PHDR *elfPhdr = loadInfo->elfPhdr; CHAR *elfInterpName = NULL; INT32 ret, i; for (i = 0; i < loadInfo->elfEhdr.elfPhNum; ++i, ++elfPhdr) { if (elfPhdr->type != LD_PT_INTERP) { continue; } if (OsVerifyELFPhdr(elfPhdr) != LOS_OK) { return -ENOEXEC; } if ((elfPhdr->fileSize > FILE_PATH_MAX) || (elfPhdr->fileSize < FILE_PATH_MIN) || (elfPhdr->offset + elfPhdr->fileSize > loadInfo->execFileLen)) { PRINT_ERR("%s[%d], The size of file is out of limit!\n", __FUNCTION__, __LINE__); return -ENOEXEC; } elfInterpName = LOS_MemAlloc(m_aucSysMem0, elfPhdr->fileSize); if (elfInterpName == NULL) { PRINT_ERR("%s[%d], Failed to allocate for elfInterpName!\n", __FUNCTION__, __LINE__); return -ENOMEM; } ret = OsReadELFInfo(loadInfo->execFD, (UINT8 *)elfInterpName, elfPhdr->fileSize, elfPhdr->offset); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); ret = -EIO; goto OUT; } if (elfInterpName[elfPhdr->fileSize - 1] != '\0') { PRINT_ERR("%s[%d], The name of interpreter is invalid!\n", __FUNCTION__, __LINE__); ret = -ENOEXEC; goto OUT; } ret = OsGetInterpEhdr(INTERP_FULL_PATH, loadInfo); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); goto OUT; } ret = OsLoadInterpProgramHdrs(loadInfo); if (ret != LOS_OK) { goto OUT; } (VOID)LOS_MemFree(m_aucSysMem0, elfInterpName); break; } return LOS_OK; OUT: (VOID)LOS_MemFree(m_aucSysMem0, elfInterpName); return ret; } STATIC UINT32 OsGetProt(UINT32 pFlags) { UINT32 prot; prot = (((pFlags & PF_R) ? PROT_READ : 0) | ((pFlags & PF_W) ? PROT_WRITE : 0) | ((pFlags & PF_X) ? PROT_EXEC : 0)); return prot; } STATIC UINT32 OsGetAllocSize(const LD_ELF_PHDR *elfPhdr, INT32 phdrNum) { const LD_ELF_PHDR *elfPhdrTemp = elfPhdr; UINTPTR addrMin = SIZE_MAX; UINTPTR addrMax = 0; UINT32 offStart = 0; UINT64 size; INT32 i; for (i = 0; i < phdrNum; ++i, ++elfPhdrTemp) { if (elfPhdrTemp->type != LD_PT_LOAD) { continue; } if (OsVerifyELFPhdr(elfPhdrTemp) != LOS_OK) { return 0; } if (elfPhdrTemp->vAddr < addrMin) { addrMin = elfPhdrTemp->vAddr; offStart = elfPhdrTemp->offset; } if ((elfPhdrTemp->vAddr + elfPhdrTemp->memSize) > addrMax) { addrMax = elfPhdrTemp->vAddr + elfPhdrTemp->memSize; } } if (OsIsBadUserAddress((VADDR_T)addrMax) || OsIsBadUserAddress((VADDR_T)addrMin) || (addrMax < addrMin)) { return 0; } size = ROUNDUP(addrMax, PAGE_SIZE) - ROUNDDOWN(addrMin, PAGE_SIZE) + ROUNDDOWN(offStart, PAGE_SIZE); return (size > UINT_MAX) ? 0 : (UINT32)size; } STATIC UINTPTR OsDoMmapFile(INT32 fd, UINTPTR addr, const LD_ELF_PHDR *elfPhdr, UINT32 prot, UINT32 flags, UINT32 mapSize) { UINTPTR mapAddr; UINT32 size; UINT32 offset = elfPhdr->offset - ROUNDOFFSET(elfPhdr->vAddr, PAGE_SIZE); addr = ROUNDDOWN(addr, PAGE_SIZE); if (mapSize != 0) { mapAddr = (UINTPTR)LOS_MMap(addr, mapSize, prot, flags, fd, offset >> PAGE_SHIFT); } else { size = elfPhdr->memSize + ROUNDOFFSET(elfPhdr->vAddr, PAGE_SIZE); if (size == 0) { return addr; } mapAddr = (UINTPTR)LOS_MMap(addr, size, prot, flags, fd, offset >> PAGE_SHIFT); } if (!LOS_IsUserAddress((VADDR_T)mapAddr)) { PRINT_ERR("%s %d, Failed to map a valid addr\n", __FUNCTION__, __LINE__); return 0; } return mapAddr; } INT32 OsGetKernelVaddr(const LosVmSpace *space, VADDR_T vaddr, VADDR_T *kvaddr) { INT32 ret; PADDR_T paddr = 0; if ((space == NULL) || (vaddr == 0) || (kvaddr == NULL)) { PRINT_ERR("%s[%d], space: %#x, vaddr: %#x\n", __FUNCTION__, __LINE__, space, vaddr); return LOS_NOK; } if (LOS_IsKernelAddress(vaddr)) { *kvaddr = vaddr; return LOS_OK; } ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, &paddr, NULL); if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to query the vaddr: %#x, status: %d\n", __FUNCTION__, __LINE__, vaddr, ret); return LOS_NOK; } *kvaddr = (VADDR_T)(UINTPTR)LOS_PaddrToKVaddr(paddr); if (*kvaddr == 0) { PRINT_ERR("%s[%d], kvaddr is null\n", __FUNCTION__, __LINE__); return LOS_NOK; } return LOS_OK; } STATIC INT32 OsSetBss(const LD_ELF_PHDR *elfPhdr, INT32 fd, UINTPTR bssStart, UINT32 bssEnd, UINT32 elfProt) { UINTPTR bssPageStart, bssStartPageAlign, bssEndPageAlign; UINTPTR mapBase; UINT32 offset, size; UINT32 bssMapSize; INT32 stackFlags; INT32 ret; vaddr_t kvaddr = 0; bssPageStart = ROUNDDOWN(bssStart, PAGE_SIZE); bssStartPageAlign = ROUNDUP(bssStart, PAGE_SIZE); bssEndPageAlign = ROUNDUP(bssEnd, PAGE_SIZE); ret = LOS_UnMMap(bssPageStart, (bssEndPageAlign - bssPageStart)); if ((ret != LOS_OK) && (bssPageStart != 0)) { PRINT_ERR("%s[%d], Failed to unmap a region, vaddr: %#x!\n", __FUNCTION__, __LINE__, bssPageStart); } ret = LOS_UserSpaceVmAlloc(OsCurrProcessGet()->vmSpace, PAGE_SIZE, (VOID **)&bssPageStart, 0, OsCvtProtFlagsToRegionFlags(elfProt, 0)); if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to do vmm alloc!\n", __FUNCTION__, __LINE__); return -ENOMEM; } ret = OsGetKernelVaddr(OsCurrProcessGet()->vmSpace, bssPageStart, &kvaddr); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EFAULT; } (VOID)memset_s((VOID *)(UINTPTR)kvaddr, PAGE_SIZE, 0, PAGE_SIZE); offset = ROUNDDOWN(elfPhdr->offset + elfPhdr->fileSize, PAGE_SIZE); size = ROUNDOFFSET(elfPhdr->offset + elfPhdr->fileSize, PAGE_SIZE); ret = OsReadELFInfo(fd, (UINT8 *)(UINTPTR)kvaddr, size, offset); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EIO; } bssMapSize = bssEndPageAlign - bssStartPageAlign; if (bssMapSize > 0) { stackFlags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS; mapBase = (UINTPTR)LOS_MMap(bssStartPageAlign, bssMapSize, elfProt, stackFlags, -1, 0); if (!LOS_IsUserAddress((VADDR_T)mapBase)) { PRINT_ERR("%s[%d], Failed to map bss\n", __FUNCTION__, __LINE__); return -ENOMEM; } } return LOS_OK; } STATIC INT32 OsMmapELFFile(INT32 fd, const LD_ELF_PHDR *elfPhdr, const LD_ELF_EHDR *elfEhdr, UINTPTR *elfLoadAddr, UINT32 mapSize, UINTPTR *loadBase) { const LD_ELF_PHDR *elfPhdrTemp = elfPhdr; UINTPTR vAddr, mapAddr, bssStart; UINT32 bssEnd, elfProt, elfFlags; INT32 ret, i; for (i = 0; i < elfEhdr->elfPhNum; ++i, ++elfPhdrTemp) { if (elfPhdrTemp->type != LD_PT_LOAD) { continue; } if (elfEhdr->elfType == LD_ET_EXEC) { if (OsVerifyELFPhdr(elfPhdrTemp) != LOS_OK) { return -ENOEXEC; } } elfProt = OsGetProt(elfPhdrTemp->flags); if ((elfProt & PROT_READ) == 0) { return -ENOEXEC; } elfFlags = MAP_PRIVATE; vAddr = elfPhdrTemp->vAddr; mapAddr = OsDoMmapFile(fd, (vAddr + *loadBase), elfPhdrTemp, elfProt, elfFlags, mapSize); if (!LOS_IsUserAddress((VADDR_T)mapAddr)) { return -ENOMEM; } #ifdef LOSCFG_DRIVERS_TZDRIVER if ((elfPhdrTemp->flags & PF_R) && (elfPhdrTemp->flags & PF_X) && !(elfPhdrTemp->flags & PF_W)) { SetVmmRegionCodeStart(vAddr + *loadBase, elfPhdrTemp->memSize); } #endif mapSize = 0; if (*elfLoadAddr == 0) { *elfLoadAddr = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE); } if ((*loadBase == 0) && (elfEhdr->elfType == LD_ET_DYN)) { *loadBase = mapAddr; elfFlags |= MAP_FIXED; } if ((elfPhdrTemp->memSize > elfPhdrTemp->fileSize) && (elfPhdrTemp->flags & PF_W)) { bssStart = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->fileSize; bssEnd = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->memSize; ret = OsSetBss(elfPhdrTemp, fd, bssStart, bssEnd, elfProt); if (ret != LOS_OK) { return ret; } } } return LOS_OK; } STATIC INT32 OsLoadInterpBinary(const ELFLoadInfo *loadInfo, UINTPTR *interpMapBase) { UINTPTR loadBase = 0; UINT32 mapSize; INT32 ret; mapSize = OsGetAllocSize(loadInfo->interpELFPhdr, loadInfo->interpELFEhdr.elfPhNum); if (mapSize == 0) { PRINT_ERR("%s[%d], Failed to get interp allocation size!\n", __FUNCTION__, __LINE__); return -EINVAL; } ret = OsMmapELFFile(loadInfo->interpFD, loadInfo->interpELFPhdr, &loadInfo->interpELFEhdr, interpMapBase, mapSize, &loadBase); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return ret; } return LOS_OK; } STATIC CHAR *OsGetParamPtr(CHAR * const *ptr, INT32 index) { UINTPTR userStrPtr = 0; INT32 ret; if (ptr == NULL) { return NULL; } if (LOS_IsKernelAddress((UINTPTR)ptr)) { return ptr[index]; } ret = LOS_GetUser(&userStrPtr, (UINTPTR *)(ptr + index)); if (ret != LOS_OK) { PRINT_ERR("%s[%d], %#x\n", __FUNCTION__, __LINE__, ptr); return NULL; } return (CHAR *)userStrPtr; } STATIC INT32 OsPutUserArg(INT32 val, const UINTPTR *sp) { INT32 ret; if (sp == NULL) { return LOS_NOK; } ret = LOS_PutUser((INT32 *)&val, (INT32 *)sp); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EFAULT; } return LOS_OK; } STATIC INT32 OsPutUserArgv(UINTPTR *strPtr, UINTPTR **sp, INT32 count) { INT32 len; INT32 i; CHAR *ptr = NULL; if ((strPtr == NULL) || (sp == NULL)) { return LOS_NOK; } for (i = 0; i < count; ++i) { /* put the addr of arg strings to user stack */ if (OsPutUserArg(*strPtr, *sp) != LOS_OK) { return LOS_NOK; } ptr = OsGetParamPtr((CHAR **)strPtr, 0); if (ptr == NULL) { return LOS_NOK; } len = LOS_StrnlenUser(ptr, PATH_MAX); if (len == 0) { return LOS_NOK; } *strPtr += len; ++(*sp); } /* put zero to end of argv */ if (OsPutUserArg(0, *sp) != LOS_OK) { return LOS_NOK; } ++(*sp); return LOS_OK; } STATIC INT32 OsCopyParams(ELFLoadInfo *loadInfo, INT32 argc, CHAR *const *argv) { CHAR *strPtr = NULL; UINT32 offset, strLen; errno_t err; INT32 ret, i; vaddr_t kvaddr = 0; if ((argc > 0) && (argv == NULL)) { return -EINVAL; } ret = OsGetKernelVaddr(loadInfo->newSpace, loadInfo->stackParamBase, &kvaddr); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EFAULT; } for (i = argc - 1; i >= 0; --i) { strPtr = OsGetParamPtr(argv, i); if (LOS_IsKernelAddress((VADDR_T)(UINTPTR)strPtr)) { strLen = strlen(strPtr) + 1; } else { strLen = LOS_StrnlenUser(strPtr, PATH_MAX); } if (strLen < 1) { continue; } offset = loadInfo->topOfMem - loadInfo->stackParamBase; if (offset < strLen) { PRINT_ERR("%s[%d], The size of param is out of limit: %#x bytes!\n", __FUNCTION__, __LINE__, USER_PARAM_BYTE_MAX); return -E2BIG; } loadInfo->topOfMem -= strLen; offset -= strLen; /* copy strings to user stack */ if (LOS_IsKernelAddress((VADDR_T)(UINTPTR)strPtr)) { err = memcpy_s((VOID *)(UINTPTR)(kvaddr + offset), strLen, strPtr, strLen); } else { err = LOS_ArchCopyFromUser((VOID *)(UINTPTR)(kvaddr + offset), strPtr, strLen); } if (err != EOK) { PRINT_ERR("%s[%d], copy strings failed! err: %d\n", __FUNCTION__, __LINE__, err); return -EFAULT; } } return LOS_OK; } STATIC INT32 OsGetParamNum(CHAR *const *argv) { CHAR *argPtr = NULL; INT32 count = 0; INT32 ret; if (argv == NULL) { return count; } argPtr = OsGetParamPtr(argv, count); while (argPtr != NULL) { ret = LOS_StrnlenUser(argPtr, PATH_MAX); if ((ret == 0) || (ret > PATH_MAX)) { PRINT_ERR("%s[%d], the len of string of argv is invalid, index: %d, len: %d\n", __FUNCTION__, __LINE__, count, ret); break; } ++count; if (count >= STRINGS_COUNT_MAX) { break; } argPtr = OsGetParamPtr(argv, count); } return count; } STATIC UINT32 OsGetRndOffset(const ELFLoadInfo *loadInfo) { UINT32 randomValue = 0; #ifdef LOSCFG_ASLR if (read(loadInfo->randomDevFD, &randomValue, sizeof(UINT32)) == sizeof(UINT32)) { randomValue &= RANDOM_MASK; } else { randomValue = 0; } #else (VOID)loadInfo; #endif return ROUNDDOWN(randomValue, PAGE_SIZE); } STATIC VOID OsGetStackProt(ELFLoadInfo *loadInfo) { LD_ELF_PHDR *elfPhdrTemp = loadInfo->elfPhdr; INT32 i; for (i = 0; i < loadInfo->elfEhdr.elfPhNum; ++i, ++elfPhdrTemp) { if (elfPhdrTemp->type == LD_PT_GNU_STACK) { loadInfo->stackProt = OsGetProt(elfPhdrTemp->flags); } } } STATIC INT32 OsSetArgParams(ELFLoadInfo *loadInfo, CHAR *const *argv, CHAR *const *envp) { UINT32 vmFlags; INT32 ret; status_t status; #ifdef LOSCFG_ASLR loadInfo->randomDevFD = open("/dev/urandom", O_RDONLY); if (loadInfo->randomDevFD < 0) { PRINT_ERR("%s: open /dev/urandom failed\n", __FUNCTION__); } #endif (VOID)OsGetStackProt(loadInfo); if (((UINT32)loadInfo->stackProt & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) { return -ENOEXEC; } loadInfo->stackTopMax = USER_STACK_TOP_MAX - OsGetRndOffset(loadInfo); loadInfo->stackBase = loadInfo->stackTopMax - USER_STACK_SIZE; loadInfo->stackSize = USER_STACK_SIZE; loadInfo->stackParamBase = loadInfo->stackTopMax - USER_PARAM_BYTE_MAX; vmFlags = OsCvtProtFlagsToRegionFlags(loadInfo->stackProt, 0); vmFlags |= VM_MAP_REGION_FLAG_STACK; status = LOS_UserSpaceVmAlloc((VOID *)loadInfo->newSpace, USER_PARAM_BYTE_MAX, (VOID **)&loadInfo->stackParamBase, 0, vmFlags); if (status != LOS_OK) { PRINT_ERR("%s[%d], Failed to create user stack! status: %d\n", __FUNCTION__, __LINE__, status); return -ENOMEM; } loadInfo->topOfMem = loadInfo->stackTopMax - sizeof(UINTPTR); loadInfo->argc = OsGetParamNum(argv); loadInfo->envc = OsGetParamNum(envp); ret = OsCopyParams(loadInfo, 1, (CHAR *const *)&loadInfo->fileName); if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to copy filename to user stack!\n", __FUNCTION__, __LINE__); return ret; } loadInfo->execName = (CHAR *)loadInfo->topOfMem; ret = OsCopyParams(loadInfo, loadInfo->envc, envp); if (ret != LOS_OK) { return ret; } ret = OsCopyParams(loadInfo, loadInfo->argc, argv); if (ret != LOS_OK) { return ret; } return LOS_OK; } STATIC INT32 OsPutParamToStack(ELFLoadInfo *loadInfo, const UINTPTR *auxVecInfo, INT32 vecIndex) { UINTPTR argStart = loadInfo->topOfMem; UINTPTR *topMem = (UINTPTR *)ROUNDDOWN(loadInfo->topOfMem, sizeof(UINTPTR)); UINTPTR *argsPtr = NULL; UINTPTR stackBase; INT32 items = (loadInfo->argc + 1) + (loadInfo->envc + 1) + 1; size_t size; INT32 stackFlags; loadInfo->topOfMem = ROUNDDOWN((UINTPTR)(topMem - vecIndex - items), STACK_ALIGN_SIZE); argsPtr = (UINTPTR *)loadInfo->topOfMem; loadInfo->stackTop = (UINTPTR)argsPtr; if ((loadInfo->stackTopMax - loadInfo->stackTop) > USER_PARAM_BYTE_MAX) { return -E2BIG; } if (OsPutUserArg(loadInfo->argc, argsPtr)) { PRINT_ERR("%s[%d], Failed to put argc to user stack!\n", __FUNCTION__, __LINE__); return -EFAULT; } argsPtr++; if ((OsPutUserArgv(&argStart, &argsPtr, loadInfo->argc) != LOS_OK) || (OsPutUserArgv(&argStart, &argsPtr, loadInfo->envc) != LOS_OK)) { PRINT_ERR("%s[%d], Failed to put argv or envp to user stack!\n", __FUNCTION__, __LINE__); return -EFAULT; } size = LOS_ArchCopyToUser(argsPtr, auxVecInfo, vecIndex * sizeof(UINTPTR)); if (size != 0) { PRINT_ERR("%s[%d], Failed to copy strings! Bytes not copied: %d\n", __FUNCTION__, __LINE__, size); return -EFAULT; } if ((loadInfo->stackSize - USER_PARAM_BYTE_MAX) > 0) { /* mmap an external region for user stack */ stackFlags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS; stackBase = (UINTPTR)LOS_MMap(loadInfo->stackBase, (loadInfo->stackSize - USER_PARAM_BYTE_MAX), loadInfo->stackProt, stackFlags, -1, 0); if (!LOS_IsUserAddress((VADDR_T)stackBase)) { PRINT_ERR("%s[%d], Failed to map user stack\n", __FUNCTION__, __LINE__); return -ENOMEM; } } return LOS_OK; } STATIC INT32 OsMakeArgsStack(ELFLoadInfo *loadInfo, UINTPTR interpMapBase) { UINTPTR auxVector[AUX_VECTOR_SIZE] = { 0 }; UINTPTR *auxVecInfo = (UINTPTR *)auxVector; INT32 vecIndex = 0; INT32 ret; #ifdef LOSCFG_KERNEL_VDSO vaddr_t vdsoLoadAddr; #endif AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHDR, loadInfo->loadAddr + loadInfo->elfEhdr.elfPhoff); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHENT, sizeof(LD_ELF_PHDR)); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHNUM, loadInfo->elfEhdr.elfPhNum); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PAGESZ, PAGE_SIZE); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_BASE, interpMapBase); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_FLAGS, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_ENTRY, loadInfo->elfEhdr.elfEntry); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_UID, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_EUID, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_GID, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_EGID, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_HWCAP, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_CLKTCK, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_SECURE, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_RANDOM, 0); AUX_VEC_ENTRY(auxVector, vecIndex, AUX_EXECFN, (UINTPTR)loadInfo->execName); #ifdef LOSCFG_KERNEL_VDSO vdsoLoadAddr = OsLoadVdso(OsCurrProcessGet()); if (vdsoLoadAddr != 0) { AUX_VEC_ENTRY(auxVector, vecIndex, AUX_SYSINFO_EHDR, vdsoLoadAddr); } #endif AUX_VEC_ENTRY(auxVector, vecIndex, AUX_NULL, 0); ret = OsPutParamToStack(loadInfo, auxVecInfo, vecIndex); if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to put param to user stack\n", __FUNCTION__, __LINE__); return ret; } return LOS_OK; } STATIC INT32 OsLoadELFSegment(ELFLoadInfo *loadInfo) { LD_ELF_PHDR *elfPhdrTemp = loadInfo->elfPhdr; UINTPTR loadBase = 0; UINTPTR interpMapBase = 0; UINT32 mapSize = 0; INT32 ret; loadInfo->loadAddr = 0; if (loadInfo->elfEhdr.elfType == LD_ET_DYN) { loadBase = EXEC_MMAP_BASE + OsGetRndOffset(loadInfo); mapSize = OsGetAllocSize(elfPhdrTemp, loadInfo->elfEhdr.elfPhNum); if (mapSize == 0) { PRINT_ERR("%s[%d], Failed to get allocation size of file: %s!\n", __FUNCTION__, __LINE__, loadInfo->fileName); return -EINVAL; } } ret = OsMmapELFFile(loadInfo->execFD, loadInfo->elfPhdr, &loadInfo->elfEhdr, &loadInfo->loadAddr, mapSize, &loadBase); if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return ret; } if (loadInfo->interpFD != INVALID_FD) { ret = OsLoadInterpBinary(loadInfo, &interpMapBase); if (ret != LOS_OK) { return ret; } loadInfo->elfEntry = loadInfo->interpELFEhdr.elfEntry + interpMapBase; loadInfo->elfEhdr.elfEntry = loadInfo->elfEhdr.elfEntry + loadBase; } else { loadInfo->elfEntry = loadInfo->elfEhdr.elfEntry; } ret = OsMakeArgsStack(loadInfo, interpMapBase); if (ret != LOS_OK) { return ret; } if (!LOS_IsUserAddress((VADDR_T)loadInfo->stackTop)) { PRINT_ERR("%s[%d], StackTop is out of limit!\n", __FUNCTION__, __LINE__); return -EINVAL; } return LOS_OK; } STATIC VOID OsFlushAspace(ELFLoadInfo *loadInfo) { LosProcessCB *processCB = OsCurrProcessGet(); OsExecDestroyTaskGroup(); loadInfo->oldSpace = processCB->vmSpace; processCB->vmSpace = loadInfo->newSpace; processCB->vmSpace->heapBase += OsGetRndOffset(loadInfo); processCB->vmSpace->heapNow = processCB->vmSpace->heapBase; processCB->vmSpace->mapBase += OsGetRndOffset(loadInfo); processCB->vmSpace->mapSize = loadInfo->stackBase - processCB->vmSpace->mapBase; LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu); } STATIC VOID OsDeInitLoadInfo(ELFLoadInfo *loadInfo) { #ifdef LOSCFG_ASLR (VOID)close(loadInfo->randomDevFD); #endif if (loadInfo->elfPhdr != NULL) { (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->elfPhdr); } if (loadInfo->interpELFPhdr != NULL) { (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->interpELFPhdr); } } STATIC VOID OsDeInitFiles(ELFLoadInfo *loadInfo) { if (loadInfo->execFD != INVALID_FD) { (VOID)close(loadInfo->execFD); } if (loadInfo->interpFD != INVALID_FD) { (VOID)close(loadInfo->interpFD); } #ifdef LOSCFG_FS_VFS delete_files_snapshot((struct files_struct *)loadInfo->oldFiles); #endif } INT32 OsLoadELFFile(ELFLoadInfo *loadInfo) { INT32 ret; ret = OsELFLoadInit(loadInfo->fileName, loadInfo); if (ret != LOS_OK) { goto OUT; } ret = OsLoadProgramHdrs(loadInfo); if (ret != LOS_OK) { goto OUT; } ret = OsReadInterpInfo(loadInfo); if (ret != LOS_OK) { goto OUT; } ret = OsSetArgParams(loadInfo, loadInfo->argv, loadInfo->envp); if (ret != LOS_OK) { goto OUT; } (VOID)OsFlushAspace(loadInfo); ret = OsLoadELFSegment(loadInfo); if (ret != LOS_OK) { OsCurrProcessGet()->vmSpace = loadInfo->oldSpace; LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu); goto OUT; } (VOID)OsDeInitLoadInfo(loadInfo); return LOS_OK; OUT: OsDeInitFiles(loadInfo); (VOID)LOS_VmSpaceFree(loadInfo->newSpace); (VOID)OsDeInitLoadInfo(loadInfo); return ret; }