Skip to content

Commit

Permalink
symbol: validate ELF format before using it
Browse files Browse the repository at this point in the history
sometime uftrace should reference the address (eg, section offset) that
the ELF field points to. Since section headers are not essential, they
can refer to insecure memory and cause errors such as segment faults.
so, add logic to validate ELF before using it.

Fixed: #412

Signed-off-by: Hanbum Park <kese111@gmail.com>
  • Loading branch information
ParkHanbum authored and namhyung committed Aug 13, 2018
1 parent dd5ce3d commit 65b18f2
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 4 deletions.
133 changes: 130 additions & 3 deletions utils/symbol-rawelf.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,83 @@
#include "utils/utils.h"
#include "utils/symbol-rawelf.h"

/*
* ELF File Header validation logic.
*/
int elf_validation(struct uftrace_elf_data *elf)
{
Elf_Ehdr *ehdr;
int eclass, data, version;
long size, offset;

ehdr = &elf->ehdr;
elf->has_shdr = false;

// validate ELF Magic.
if (memcmp(ehdr, ELFMAG, SELFMAG)) {
pr_dbg2("ELF Signature not matched\n");
return -1;
}

// validate some field of elf header.
eclass = (int) ehdr->e_ident[EI_CLASS];
data = (int) ehdr->e_ident[EI_DATA];
version = (int) ehdr->e_ident[EI_VERSION];

if (!(eclass > ELFCLASSNONE && eclass < ELFCLASSNUM)
|| eclass != get_elf_class()) {
pr_dbg2("Invalid eclass : [%d]\n", eclass);
return -1;
}

if (!(data > ELFDATANONE && data < ELFDATANUM)
|| data != get_elf_endian()) {
pr_dbg2("Invalid endian : [%d]\n", data);
return -1;
}

if (!(version > EV_NONE && version < EV_NUM)) {
pr_dbg2("Invalid ELF version : [%d]\n", version);
return -1;
}

if (ehdr->e_phnum == 0 || ehdr->e_phentsize == 0) {
pr_dbg2("Invalid Program header. Num:[%d] Size:[%d]\n",
ehdr->e_phnum, ehdr->e_phentsize);
return -1;
}

if (ehdr->e_shnum > 0 && ehdr->e_shentsize == 0) {
pr_dbg2("Section Header entry size cannot be 0.\n");
return -1;
}

// validate program header offset.
size = (long)elf->file_size;
offset = ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize;

if (offset > size) {
pr_dbg2("Invalid Program Header offset."\
"offset:[%lu], size:[%lu]\n"\
,offset, size);
return -1;
}

// validate sectio header offset.
offset = ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize;

if (offset > size) {
pr_dbg2("Invalid Program Header offset."\
"offset:[%lu], size:[%lu]\n"\
,offset, size);
elf->has_shdr = false;
}
else
elf->has_shdr = true;

return 0;
}

int elf_init(const char *filename, struct uftrace_elf_data *elf)
{
struct stat stbuf;
Expand All @@ -33,6 +110,9 @@ int elf_init(const char *filename, struct uftrace_elf_data *elf)

memcpy(&elf->ehdr, elf->file_map, sizeof(elf->ehdr));

if (elf_validation(elf) < 0)
goto err;

return 0;

err_close:
Expand Down Expand Up @@ -60,9 +140,10 @@ void elf_get_strtab(struct uftrace_elf_data *elf,
struct uftrace_elf_iter *iter,
int shidx)
{
Elf_Shdr *shdr = elf->file_map + elf->ehdr.e_shoff;

iter->strtab = elf->file_map + shdr[shidx].sh_offset;
if (elf->has_shdr) {
Elf_Shdr *shdr = elf->file_map + elf->ehdr.e_shoff;
iter->strtab = elf->file_map + shdr[shidx].sh_offset;
}
}

void elf_get_secdata(struct uftrace_elf_data *elf,
Expand All @@ -79,4 +160,50 @@ void elf_read_secdata(struct uftrace_elf_data *elf,
memcpy(buf, &iter->data[offset], len);
}

#ifdef UNIT_TEST

static int get_self(struct uftrace_elf_data *elf)
{
char path[PATH_MAX];
const char *link = "/proc/self/exe";

if (readlink(link, path, PATH_MAX-1) < 0)
return -1;

if (elf_init(path, elf) < 0)
return -1;

return 0;
}

TEST_CASE(elf_test)
{
struct uftrace_elf_data elf;
struct uftrace_elf_iter iter;
Elf_Ehdr *ehdr;
unsigned int count;

if (get_self(&elf) < 0)
return TEST_BAD;

ehdr = &elf.ehdr;

count = 0;
elf_for_each_phdr(&elf, &iter) {
count++;
}

TEST_EQ(ehdr->e_phnum, count);

count = 0;
elf_for_each_shdr(&elf, &iter) {
count++;
}
TEST_EQ(ehdr->e_shnum, count);

TEST_EQ(elf_validation(&elf), 0);
return TEST_OK;
}

#endif /* UNIT_TEST */
#endif /* HAVE_LIBELF */
3 changes: 2 additions & 1 deletion utils/symbol-rawelf.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct uftrace_elf_data {
size_t file_size;
Elf_Ehdr ehdr;
unsigned long flags;
bool has_shdr;
};

struct uftrace_elf_iter {
Expand Down Expand Up @@ -91,7 +92,7 @@ struct uftrace_elf_iter {
#define elf_for_each_shdr(elf, iter) \
for (elf_get_strtab((elf), (iter), (elf)->ehdr.e_shstrndx), \
(iter)->i = 0, (iter)->nr = (elf)->ehdr.e_shnum; \
(iter)->i < (iter)->nr && \
(iter)->i < (iter)->nr && (elf)->has_shdr && \
memcpy(&(iter)->shdr, \
(elf)->file_map + (elf)->ehdr.e_shoff + \
(iter)->i * (elf)->ehdr.e_shentsize, \
Expand Down
16 changes: 16 additions & 0 deletions utils/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ extern void setup_signal(void);
# define ELFDATA2MSB 2 /* 2's complement, big endian */
#endif

#ifndef ELFCLASS32
# define ELFCLASSNONE 0
# define ELFCLASS32 1
# define ELFCLASS64 2
#endif

static inline int get_elf_endian(void)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
Expand All @@ -227,6 +233,16 @@ static inline int get_elf_endian(void)
#endif
}

static inline int get_elf_class(void)
{
if (sizeof(long) == 4)
return ELFCLASS32;
else if (sizeof(long) == 8)
return ELFCLASS64;
else
return ELFCLASSNONE;
}

struct uftrace_time_range {
uint64_t first;
uint64_t start;
Expand Down

0 comments on commit 65b18f2

Please sign in to comment.