Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/include/openamp/elf_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ struct elf32_info {
Elf32_Phdr *phdrs;
Elf32_Shdr *shdrs;
void *shstrtab;
size_t shstrtab_size;
};

struct elf64_info {
Expand All @@ -298,6 +299,7 @@ struct elf64_info {
Elf64_Phdr *phdrs;
Elf64_Shdr *shdrs;
void *shstrtab;
size_t shstrtab_size;
};

#define ELF_STATE_INIT 0x0L
Expand Down
149 changes: 139 additions & 10 deletions lib/remoteproc/elf_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,46 @@
*/

#include <string.h>
#include <stdint.h>
#include <metal/alloc.h>
#include <metal/log.h>
#include <openamp/elf_loader.h>
#include <openamp/remoteproc.h>

static int elf_mul_size(size_t a, size_t b, size_t *out)
{
if (a != 0 && b > SIZE_MAX / a)
return -RPROC_EINVAL;

*out = a * b;
return 0;
}

static int elf_add_size(size_t a, size_t b, size_t *out)
{
if (b > SIZE_MAX - a)
return -RPROC_EINVAL;

*out = a + b;
return 0;
}

static int elf_range_in_chunk(size_t chunk_offset, size_t chunk_len,
size_t range_offset, size_t range_len)
{
size_t chunk_end;
size_t range_end;

if (elf_add_size(chunk_offset, chunk_len, &chunk_end) < 0 ||
elf_add_size(range_offset, range_len, &range_end) < 0)
return -RPROC_EINVAL;

if (chunk_offset > range_offset || chunk_end < range_end)
return 0;

return 1;
}

static int elf_is_64(const void *elf_info)
{
const unsigned char *tmp = elf_info;
Expand Down Expand Up @@ -122,6 +157,43 @@ static int elf_shstrndx(const void *elf_info)
}
}

static int elf_validate_header(const void *elf_info)
{
if (elf_is_64(elf_info) == 0) {
const Elf32_Ehdr *ehdr = elf_info;

if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
return -RPROC_EINVAL;
if (ehdr->e_ehsize != sizeof(Elf32_Ehdr))
return -RPROC_EINVAL;
if (ehdr->e_phnum != 0 &&
ehdr->e_phentsize != sizeof(Elf32_Phdr))
return -RPROC_EINVAL;
if (ehdr->e_shnum != 0 &&
ehdr->e_shentsize != sizeof(Elf32_Shdr))
return -RPROC_EINVAL;
if (ehdr->e_shnum != 0 && ehdr->e_shstrndx >= ehdr->e_shnum)
return -RPROC_EINVAL;
} else {
const Elf64_Ehdr *ehdr = elf_info;

if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
return -RPROC_EINVAL;
if (ehdr->e_ehsize != sizeof(Elf64_Ehdr))
return -RPROC_EINVAL;
if (ehdr->e_phnum != 0 &&
ehdr->e_phentsize != sizeof(Elf64_Phdr))
return -RPROC_EINVAL;
if (ehdr->e_shnum != 0 &&
ehdr->e_shentsize != sizeof(Elf64_Shdr))
return -RPROC_EINVAL;
if (ehdr->e_shnum != 0 && ehdr->e_shstrndx >= ehdr->e_shnum)
return -RPROC_EINVAL;
}

return 0;
}

static void **elf_phtable_ptr(void *elf_info)
{
if (elf_is_64(elf_info) == 0) {
Expand Down Expand Up @@ -161,6 +233,15 @@ static void **elf_shstrtab_ptr(void *elf_info)
}
}

static void elf_set_shstrtab_size(void *elf_info, size_t size)
{
if (elf_is_64(elf_info) == 0) {
((struct elf32_info *)elf_info)->shstrtab_size = size;
} else {
((struct elf64_info *)elf_info)->shstrtab_size = size;
}
}

static int *elf_load_state(void *elf_info)
{
if (elf_is_64(elf_info) == 0) {
Expand All @@ -174,6 +255,25 @@ static int *elf_load_state(void *elf_info)
}
}

static int elf_section_name_matches(const char *name, const char *name_table,
size_t name_table_size, size_t sh_name)
{
size_t name_len;
size_t remaining;
const char *candidate;

if (sh_name >= name_table_size)
return 0;

remaining = name_table_size - sh_name;
name_len = strlen(name);
if (name_len >= remaining)
return 0;

candidate = name_table + sh_name;
return memcmp(name, candidate, name_len + 1) == 0;
}

static void elf_parse_segment(void *elf_info, const void *elf_phdr,
unsigned int *p_type, size_t *p_offset,
metal_phys_addr_t *p_vaddr,
Expand Down Expand Up @@ -242,17 +342,21 @@ static void *elf_get_section_from_name(void *elf_info, const char *name)
{
unsigned int i;
const char *name_table;
size_t name_table_size;

if (elf_is_64(elf_info) == 0) {
struct elf32_info *einfo = elf_info;
Elf32_Ehdr *ehdr = &einfo->ehdr;
Elf32_Shdr *shdr = einfo->shdrs;

name_table = einfo->shstrtab;
name_table_size = einfo->shstrtab_size;
if (!shdr || !name_table)
return NULL;
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
if (strcmp(name, name_table + shdr->sh_name))
if (!elf_section_name_matches(name, name_table,
name_table_size,
shdr->sh_name))
continue;
else
return shdr;
Expand All @@ -263,10 +367,13 @@ static void *elf_get_section_from_name(void *elf_info, const char *name)
Elf64_Shdr *shdr = einfo->shdrs;

name_table = einfo->shstrtab;
name_table_size = einfo->shstrtab_size;
if (!shdr || !name_table)
return NULL;
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
if (strcmp(name, name_table + shdr->sh_name))
if (!elf_section_name_matches(name, name_table,
name_table_size,
shdr->sh_name))
continue;
else
return shdr;
Expand Down Expand Up @@ -420,6 +527,8 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
memset(*img_info, 0, infosize);
}
memcpy(*img_info, img_data, tmpsize);
if (elf_validate_header(*img_info) < 0)
return -RPROC_EINVAL;
load_state = elf_load_state(*img_info);
*load_state = ELF_STATE_WAIT_FOR_PHDRS;
last_load_state = ELF_STATE_WAIT_FOR_PHDRS;
Expand All @@ -433,14 +542,21 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
if (*load_state == ELF_STATE_WAIT_FOR_PHDRS) {
size_t phdrs_size;
size_t phdrs_offset;
int range_in_chunk;
void **phdrs;
const void *img_phdrs;

metal_log(METAL_LOG_DEBUG, "Loading ELF program header.\r\n");
phdrs_offset = elf_phoff(*img_info);
phdrs_size = elf_phnum(*img_info) * elf_phentsize(*img_info);
if (offset > phdrs_offset ||
offset + len < phdrs_offset + phdrs_size) {
if (elf_mul_size(elf_phnum(*img_info),
elf_phentsize(*img_info),
&phdrs_size) < 0)
return -RPROC_EINVAL;
range_in_chunk = elf_range_in_chunk(offset, len, phdrs_offset,
phdrs_size);
if (range_in_chunk < 0)
return range_in_chunk;
if (!range_in_chunk) {
*noffset = phdrs_offset;
*nlen = phdrs_size;
return *load_state;
Expand All @@ -460,6 +576,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
if ((*load_state & ELF_STATE_WAIT_FOR_SHDRS) != 0) {
size_t shdrs_size;
size_t shdrs_offset;
int range_in_chunk;
void **shdrs;
const void *img_shdrs;

Expand All @@ -471,9 +588,15 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
*nlen = 0;
return *load_state;
}
shdrs_size = elf_shnum(*img_info) * elf_shentsize(*img_info);
if (offset > shdrs_offset ||
offset + len < shdrs_offset + shdrs_size) {
if (elf_mul_size(elf_shnum(*img_info),
elf_shentsize(*img_info),
&shdrs_size) < 0)
return -RPROC_EINVAL;
range_in_chunk = elf_range_in_chunk(offset, len, shdrs_offset,
shdrs_size);
if (range_in_chunk < 0)
return range_in_chunk;
if (!range_in_chunk) {
*noffset = shdrs_offset;
*nlen = shdrs_size;
return *load_state;
Expand All @@ -498,6 +621,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
int shstrndx;
void *shdr;
void **shstrtab;
int range_in_chunk;

metal_log(METAL_LOG_DEBUG, "Loading ELF shstrtab.\r\n");
shstrndx = elf_shstrndx(*img_info);
Expand All @@ -508,8 +632,12 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
NULL, &shstrtab_offset,
&shstrtab_size, NULL, NULL,
NULL, NULL);
if (offset > shstrtab_offset ||
offset + len < shstrtab_offset + shstrtab_size) {
range_in_chunk = elf_range_in_chunk(offset, len,
shstrtab_offset,
shstrtab_size);
if (range_in_chunk < 0)
return range_in_chunk;
if (!range_in_chunk) {
*noffset = shstrtab_offset;
*nlen = shstrtab_size;
return *load_state;
Expand All @@ -520,6 +648,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len,
*shstrtab = metal_allocate_memory(shstrtab_size);
if (!*shstrtab)
return -RPROC_ENOMEM;
elf_set_shstrtab_size(*img_info, shstrtab_size);
memcpy(*shstrtab,
(const char *)img_data + shstrtab_offset,
shstrtab_size);
Expand Down
2 changes: 1 addition & 1 deletion lib/remoteproc/remoteproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ int remoteproc_load(struct remoteproc *rproc, const char *path,
} else if ((ret & RPROC_LOADER_READY_TO_LOAD) != 0) {
if (nlen == 0)
break;
else if ((noffset > (offset + len)) &&
else if ((noffset > offset && noffset - offset > len) &&
(store_ops->features & SUPPORT_SEEK) == 0) {
/* Required data is not continued, however
* seek is not supported, stop to load
Expand Down
Loading