* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 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 "fcntl.h"
#include "fs/fd_table.h"
#include "fs/file.h"
#include "fs/fs_operation.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"
#include "los_vm_lock.h"
#ifdef LOSCFG_KERNEL_VDSO
#include "los_vdso.h"
#endif
#ifdef LOSCFG_DRIVERS_TZDRIVER
#include "tzdriver.h"
#endif
STATIC BOOL g_srandInit;
STATIC INT32 OsELFOpen(const CHAR *fileName, INT32 oflags)
{
INT32 ret;
INT32 procFd;
procFd = AllocProcessFd();
if (procFd < 0) {
return -EMFILE;
}
if (oflags & O_CLOEXEC) {
SetCloexecFlag(procFd);
}
ret = open(fileName, oflags);
if (ret < 0) {
FreeProcessFd(procFd);
return -get_errno();
}
AssociateSystemFd(procFd, ret);
return procFd;
}
STATIC INT32 OsELFClose(INT32 procFd)
{
INT32 ret;
INT32 sysfd = DisassociateProcessFd(procFd);
if (sysfd < 0) {
return -EBADF;
}
ret = close(sysfd);
if (ret < 0) {
AssociateSystemFd(procFd, sysfd);
return -get_errno();
}
FreeProcessFd(procFd);
return ret;
}
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 procfd, UINT8 *buffer, size_t readSize, off_t offset)
{
ssize_t byteNum;
off_t returnPos;
INT32 fd = GetAssociatedSystemFd(procfd);
if (fd < 0) {
PRINT_ERR("%s[%d], Invalid procfd!\n", __FUNCTION__, __LINE__);
return LOS_NOK;
}
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 VOID OsLoadInit(ELFLoadInfo *loadInfo)
{
#ifdef LOSCFG_FS_VFS
const struct files_struct *oldFiles = OsCurrProcessGet()->files;
loadInfo->oldFiles = (UINTPTR)create_files_snapshot(oldFiles);
#else
loadInfo->oldFiles = NULL;
#endif
loadInfo->execInfo.procfd = INVALID_FD;
loadInfo->interpInfo.procfd = INVALID_FD;
}
STATIC INT32 OsReadEhdr(const CHAR *fileName, ELFInfo *elfInfo, BOOL isExecFile)
{
INT32 ret;
ret = OsGetFileLength(&elfInfo->fileLen, fileName);
if (ret != LOS_OK) {
return -ENOENT;
}
ret = OsELFOpen(fileName, O_RDONLY | O_EXECVE | O_CLOEXEC);
if (ret < 0) {
PRINT_ERR("%s[%d], Failed to open ELF file: %s!\n", __FUNCTION__, __LINE__, fileName);
return ret;
}
elfInfo->procfd = ret;
#ifdef LOSCFG_DRIVERS_TZDRIVER
if (isExecFile) {
struct file *filep = NULL;
ret = fs_getfilep(GetAssociatedSystemFd(elfInfo->procfd), &filep);
if (ret) {
PRINT_ERR("%s[%d], Failed to get struct file %s!\n", __FUNCTION__, __LINE__, fileName);
return ret;
}
OsCurrProcessGet()->execVnode = filep->f_vnode;
}
#endif
ret = OsReadELFInfo(elfInfo->procfd, (UINT8 *)&elfInfo->elfEhdr, sizeof(LD_ELF_EHDR), 0);
if (ret != LOS_OK) {
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
return -EIO;
}
ret = OsVerifyELFEhdr(&elfInfo->elfEhdr, elfInfo->fileLen);
if (ret != LOS_OK) {
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
return isExecFile ? -ENOEXEC : -ELIBBAD;
}
return LOS_OK;
}
STATIC INT32 OsReadPhdrs(ELFInfo *elfInfo, BOOL isExecFile)
{
LD_ELF_EHDR *elfEhdr = &elfInfo->elfEhdr;
UINT32 size;
INT32 ret;
if (elfEhdr->elfPhNum < 1) {
goto OUT;
}
if (elfEhdr->elfPhEntSize != sizeof(LD_ELF_PHDR)) {
goto OUT;
}
size = sizeof(LD_ELF_PHDR) * elfEhdr->elfPhNum;
if ((elfEhdr->elfPhoff + size) > elfInfo->fileLen) {
goto OUT;
}
elfInfo->elfPhdr = LOS_MemAlloc(m_aucSysMem0, size);
if (elfInfo->elfPhdr == NULL) {
PRINT_ERR("%s[%d], Failed to allocate for elfPhdr!\n", __FUNCTION__, __LINE__);
return -ENOMEM;
}
ret = OsReadELFInfo(elfInfo->procfd, (UINT8 *)elfInfo->elfPhdr, size, elfEhdr->elfPhoff);
if (ret != LOS_OK) {
(VOID)LOS_MemFree(m_aucSysMem0, elfInfo->elfPhdr);
elfInfo->elfPhdr = NULL;
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
return -EIO;
}
return LOS_OK;
OUT:
PRINT_ERR("%s[%d], elf file is bad!\n", __FUNCTION__, __LINE__);
return isExecFile ? -ENOEXEC : -ELIBBAD;
}
STATIC INT32 OsReadInterpInfo(ELFLoadInfo *loadInfo)
{
LD_ELF_PHDR *elfPhdr = loadInfo->execInfo.elfPhdr;
CHAR *elfInterpName = NULL;
INT32 ret, i;
for (i = 0; i < loadInfo->execInfo.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->execInfo.fileLen)) {
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->execInfo.procfd, (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 = OsReadEhdr(INTERP_FULL_PATH, &loadInfo->interpInfo, FALSE);
if (ret != LOS_OK) {
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
goto OUT;
}
ret = OsReadPhdrs(&loadInfo->interpInfo, FALSE);
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(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 bssStartPageAlign, bssEndPageAlign;
UINTPTR mapBase;
UINT32 bssMapSize;
INT32 stackFlags;
INT32 ret;
bssStartPageAlign = ROUNDUP(bssStart, PAGE_SIZE);
bssEndPageAlign = ROUNDUP(bssEnd, PAGE_SIZE);
ret = LOS_UserMemClear((VOID *)bssStart, PAGE_SIZE - ROUNDOFFSET(bssStart, PAGE_SIZE));
if (ret != 0) {
PRINT_ERR("%s[%d], Failed to clear bss\n", __FUNCTION__, __LINE__);
return -EFAULT;
}
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 procfd, 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;
INT32 fd = GetAssociatedSystemFd(procfd);
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 | MAP_FIXED;
vAddr = elfPhdrTemp->vAddr;
if ((vAddr == 0) && (*loadBase == 0)) {
elfFlags &= ~MAP_FIXED;
}
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;
}
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(ELFLoadInfo *loadInfo, UINTPTR *interpMapBase)
{
UINTPTR loadBase = 0;
UINT32 mapSize;
INT32 ret;
mapSize = OsGetAllocSize(loadInfo->interpInfo.elfPhdr, loadInfo->interpInfo.elfEhdr.elfPhNum);
if (mapSize == 0) {
PRINT_ERR("%s[%d], Failed to get interp allocation size!\n", __FUNCTION__, __LINE__);
return -EINVAL;
}
ret = OsMmapELFFile(loadInfo->interpInfo.procfd, loadInfo->interpInfo.elfPhdr, &loadInfo->interpInfo.elfEhdr,
interpMapBase, mapSize, &loadBase);
if (ret != LOS_OK) {
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
}
return ret;
}
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) {
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);
}
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;
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;
}
UINT32 OsGetRndOffset(INT32 randomDevFD)
{
UINT32 randomValue = 0;
#ifdef LOSCFG_ASLR
if (read(randomDevFD, &randomValue, sizeof(UINT32)) == sizeof(UINT32)) {
randomValue &= RANDOM_MASK;
} else {
randomValue = (UINT32)random() & RANDOM_MASK;
}
#else
(VOID)randomDevFD;
#endif
return ROUNDDOWN(randomValue, PAGE_SIZE);
}
STATIC VOID OsGetStackProt(ELFLoadInfo *loadInfo)
{
LD_ELF_PHDR *elfPhdrTemp = loadInfo->execInfo.elfPhdr;
INT32 i;
for (i = 0; i < loadInfo->execInfo.elfEhdr.elfPhNum; ++i, ++elfPhdrTemp) {
if (elfPhdrTemp->type == LD_PT_GNU_STACK) {
loadInfo->stackProt = OsGetProt(elfPhdrTemp->flags);
}
}
}
STATIC UINT32 OsStackAlloc(LosVmSpace *space, VADDR_T vaddr, UINT32 vsize, UINT32 psize, UINT32 regionFlags)
{
LosVmPage *vmPage = NULL;
VADDR_T *kvaddr = NULL;
LosVmMapRegion *region = NULL;
VADDR_T vaddrTemp;
PADDR_T paddrTemp;
UINT32 len;
(VOID)LOS_MuxAcquire(&space->regionMux);
kvaddr = LOS_PhysPagesAllocContiguous(psize >> PAGE_SHIFT);
if (kvaddr == NULL) {
goto OUT;
}
region = LOS_RegionAlloc(space, vaddr, vsize, regionFlags | VM_MAP_REGION_FLAG_FIXED, 0);
if (region == NULL) {
goto PFREE;
}
len = psize;
vaddrTemp = region->range.base + vsize - psize;
paddrTemp = LOS_PaddrQuery(kvaddr);
while (len > 0) {
vmPage = LOS_VmPageGet(paddrTemp);
LOS_AtomicInc(&vmPage->refCounts);
(VOID)LOS_ArchMmuMap(&space->archMmu, vaddrTemp, paddrTemp, 1, region->regionFlags);
paddrTemp += PAGE_SIZE;
vaddrTemp += PAGE_SIZE;
len -= PAGE_SIZE;
}
(VOID)LOS_MuxRelease(&space->regionMux);
return LOS_OK;
PFREE:
(VOID)LOS_PhysPagesFreeContiguous(kvaddr, psize >> PAGE_SHIFT);
OUT:
(VOID)LOS_MuxRelease(&space->regionMux);
return LOS_NOK;
}
STATIC INT32 OsSetArgParams(ELFLoadInfo *loadInfo, CHAR *const *argv, CHAR *const *envp)
{
UINT32 vmFlags;
INT32 ret;
loadInfo->randomDevFD = open("/dev/urandom", O_RDONLY);
if (loadInfo->randomDevFD < 0) {
if (!g_srandInit) {
srand((UINT32)time(NULL));
g_srandInit = TRUE;
}
}
(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->randomDevFD);
loadInfo->stackBase = loadInfo->stackTopMax - USER_STACK_SIZE;
loadInfo->stackSize = USER_STACK_SIZE;
loadInfo->stackParamBase = loadInfo->stackTopMax - USER_PARAM_BYTE_MAX;
vmFlags = OsCvtProtFlagsToRegionFlags(loadInfo->stackProt, MAP_FIXED);
vmFlags |= VM_MAP_REGION_FLAG_STACK;
ret = OsStackAlloc((VOID *)loadInfo->newSpace, loadInfo->stackBase, USER_STACK_SIZE,
USER_PARAM_BYTE_MAX, vmFlags);
if (ret != LOS_OK) {
PRINT_ERR("%s[%d], Failed to alloc memory for user stack!\n", __FUNCTION__, __LINE__);
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;
}
loadInfo->argStart = loadInfo->topOfMem;
return LOS_OK;
}
STATIC INT32 OsPutParamToStack(ELFLoadInfo *loadInfo, const UINTPTR *auxVecInfo, INT32 vecIndex)
{
UINTPTR *topMem = (UINTPTR *)ROUNDDOWN(loadInfo->topOfMem, sizeof(UINTPTR));
UINTPTR *argsPtr = NULL;
INT32 items = (loadInfo->argc + 1) + (loadInfo->envc + 1) + 1;
size_t size;
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(&loadInfo->argStart, &argsPtr, loadInfo->argc) != LOS_OK) ||
(OsPutUserArgv(&loadInfo->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;
}
return LOS_OK;
}
STATIC INT32 OsGetRndNum(const ELFLoadInfo *loadInfo, UINT32 *rndVec, UINT32 vecSize)
{
UINT32 randomValue = 0;
UINT32 i, ret;
for (i = 0; i < vecSize; ++i) {
ret = read(loadInfo->randomDevFD, &randomValue, sizeof(UINT32));
if (ret != sizeof(UINT32)) {
rndVec[i] = (UINT32)random();
continue;
}
rndVec[i] = randomValue;
}
return LOS_OK;
}
STATIC INT32 OsMakeArgsStack(ELFLoadInfo *loadInfo, UINTPTR interpMapBase)
{
UINTPTR auxVector[AUX_VECTOR_SIZE] = { 0 };
UINTPTR *auxVecInfo = (UINTPTR *)auxVector;
INT32 vecIndex = 0;
UINT32 rndVec[RANDOM_VECTOR_SIZE];
UINTPTR rndVecStart;
INT32 ret;
#ifdef LOSCFG_KERNEL_VDSO
vaddr_t vdsoLoadAddr;
#endif
ret = OsGetRndNum(loadInfo, rndVec, sizeof(rndVec));
if (ret != LOS_OK) {
return ret;
}
loadInfo->topOfMem -= sizeof(rndVec);
rndVecStart = loadInfo->topOfMem;
ret = LOS_ArchCopyToUser((VOID *)loadInfo->topOfMem, rndVec, sizeof(rndVec));
if (ret != 0) {
return -EFAULT;
}
AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHDR, loadInfo->loadAddr + loadInfo->execInfo.elfEhdr.elfPhoff);
AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHENT, sizeof(LD_ELF_PHDR));
AUX_VEC_ENTRY(auxVector, vecIndex, AUX_PHNUM, loadInfo->execInfo.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->execInfo.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, rndVecStart);
AUX_VEC_ENTRY(auxVector, vecIndex, AUX_EXECFN, (UINTPTR)loadInfo->execName);
#ifdef LOSCFG_KERNEL_VDSO
vdsoLoadAddr = OsVdsoLoad(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->execInfo.elfPhdr;
UINTPTR loadBase = 0;
UINTPTR interpMapBase = 0;
UINT32 mapSize = 0;
INT32 ret;
loadInfo->loadAddr = 0;
if (loadInfo->execInfo.elfEhdr.elfType == LD_ET_DYN) {
loadBase = EXEC_MMAP_BASE + OsGetRndOffset(loadInfo->randomDevFD);
mapSize = OsGetAllocSize(elfPhdrTemp, loadInfo->execInfo.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->execInfo.procfd, loadInfo->execInfo.elfPhdr, &loadInfo->execInfo.elfEhdr,
&loadInfo->loadAddr, mapSize, &loadBase);
OsELFClose(loadInfo->execInfo.procfd);
loadInfo->execInfo.procfd = INVALID_FD;
if (ret != LOS_OK) {
PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__);
return ret;
}
if (loadInfo->interpInfo.procfd != INVALID_FD) {
ret = OsLoadInterpBinary(loadInfo, &interpMapBase);
OsELFClose(loadInfo->interpInfo.procfd);
loadInfo->interpInfo.procfd = INVALID_FD;
if (ret != LOS_OK) {
return ret;
}
loadInfo->elfEntry = loadInfo->interpInfo.elfEhdr.elfEntry + interpMapBase;
loadInfo->execInfo.elfEhdr.elfEntry = loadInfo->execInfo.elfEhdr.elfEntry + loadBase;
} else {
loadInfo->elfEntry = loadInfo->execInfo.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)
{
loadInfo->oldSpace = OsExecProcessVmSpaceReplace(loadInfo->newSpace, loadInfo->stackBase, loadInfo->randomDevFD);
}
STATIC VOID OsDeInitLoadInfo(ELFLoadInfo *loadInfo)
{
(VOID)close(loadInfo->randomDevFD);
if (loadInfo->execInfo.elfPhdr != NULL) {
(VOID)LOS_MemFree(m_aucSysMem0, loadInfo->execInfo.elfPhdr);
}
if (loadInfo->interpInfo.elfPhdr != NULL) {
(VOID)LOS_MemFree(m_aucSysMem0, loadInfo->interpInfo.elfPhdr);
}
}
STATIC VOID OsDeInitFiles(ELFLoadInfo *loadInfo)
{
if (loadInfo->execInfo.procfd != INVALID_FD) {
(VOID)OsELFClose(loadInfo->execInfo.procfd);
}
if (loadInfo->interpInfo.procfd != INVALID_FD) {
(VOID)OsELFClose(loadInfo->interpInfo.procfd);
}
#ifdef LOSCFG_FS_VFS
delete_files_snapshot((struct files_struct *)loadInfo->oldFiles);
#endif
}
INT32 OsLoadELFFile(ELFLoadInfo *loadInfo)
{
INT32 ret;
OsLoadInit(loadInfo);
ret = OsReadEhdr(loadInfo->fileName, &loadInfo->execInfo, TRUE);
if (ret != LOS_OK) {
goto OUT;
}
ret = OsReadPhdrs(&loadInfo->execInfo, TRUE);
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;
}
OsFlushAspace(loadInfo);
ret = OsLoadELFSegment(loadInfo);
if (ret != LOS_OK) {
OsExecProcessVmSpaceRestore(loadInfo->oldSpace);
goto OUT;
}
OsDeInitLoadInfo(loadInfo);
return LOS_OK;
OUT:
OsDeInitFiles(loadInfo);
(VOID)LOS_VmSpaceFree(loadInfo->newSpace);
(VOID)OsDeInitLoadInfo(loadInfo);
return ret;
}