diff options
author | kaiw <kaiw@FreeBSD.org> | 2010-07-21 10:25:02 +0000 |
---|---|---|
committer | kaiw <kaiw@FreeBSD.org> | 2010-07-21 10:25:02 +0000 |
commit | 49a7d97ce43a82f9cfa863a35ca5cc928d7caf1e (patch) | |
tree | 49e27eb10d72ea7dccf8d6dc7232254e881bb052 /lib | |
parent | 800d46f6e4936589dbbae5ddea8fe956c0cc7500 (diff) | |
download | FreeBSD-src-49a7d97ce43a82f9cfa863a35ca5cc928d7caf1e.zip FreeBSD-src-49a7d97ce43a82f9cfa863a35ca5cc928d7caf1e.tar.gz |
Perform additional checks when translating between file and memory
representations of ELF types.
The ELF(3) API allows applications to request a conversion that is
`in-place', i.e., with source and destinations data buffers being
the same. However, the file and memory sizes of ELF sections that
have additional internal structure, such as those of type `Elf_Note',
or `Elf_GNU_Hash_Header', can be determined only known after the
type-specific headers that comprise the first few words in these
sections are read and translated.
Pass in the size of destination buffer to type translation routines
in "libelf_convert.m4" and have these routines return an error code
if the translated data would not fit inside the destination buffer.
Obtained from: elftoolchain
MFC after: 1 month
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libelf/_libelf.h | 4 | ||||
-rw-r--r-- | lib/libelf/elf_data.c | 13 | ||||
-rw-r--r-- | lib/libelf/elf_scn.c | 5 | ||||
-rw-r--r-- | lib/libelf/libelf_convert.m4 | 132 | ||||
-rw-r--r-- | lib/libelf/libelf_ehdr.c | 9 | ||||
-rw-r--r-- | lib/libelf/libelf_phdr.c | 4 | ||||
-rw-r--r-- | lib/libelf/libelf_xlate.c | 12 |
7 files changed, 114 insertions, 65 deletions
diff --git a/lib/libelf/_libelf.h b/lib/libelf/_libelf.h index 3053bce..4514359 100644 --- a/lib/libelf/_libelf.h +++ b/lib/libelf/_libelf.h @@ -171,8 +171,8 @@ void *_libelf_ehdr(Elf *_e, int _elfclass, int _allocate); int _libelf_falign(Elf_Type _t, int _elfclass); size_t _libelf_fsize(Elf_Type _t, int _elfclass, unsigned int _version, size_t count); -void (*_libelf_get_translator(Elf_Type _t, int _direction, int _elfclass)) - (char *_dst, char *_src, size_t _cnt, int _byteswap); +int (*_libelf_get_translator(Elf_Type _t, int _direction, int _elfclass)) + (char *_dst, size_t dsz, char *_src, size_t _cnt, int _byteswap); void *_libelf_getphdr(Elf *_e, int _elfclass); void *_libelf_getshdr(Elf_Scn *_scn, int _elfclass); void _libelf_init_elf(Elf *_e, Elf_Kind _kind); diff --git a/lib/libelf/elf_data.c b/lib/libelf/elf_data.c index 0fbbae0..9241b09 100644 --- a/lib/libelf/elf_data.c +++ b/lib/libelf/elf_data.c @@ -43,7 +43,7 @@ elf_getdata(Elf_Scn *s, Elf_Data *d) int elfclass, elftype; unsigned int sh_type; uint64_t sh_align, sh_offset, sh_size; - void (*xlate)(char *_d, char *_s, size_t _c, int _swap); + int (*xlate)(char *_d, size_t _dsz, char *_s, size_t _c, int _swap); if (s == NULL || (e = s->s_elf) == NULL || e->e_kind != ELF_K_ELF || (d != NULL && s != d->d_scn)) { @@ -125,11 +125,16 @@ elf_getdata(Elf_Scn *s, Elf_Data *d) } d->d_flags |= LIBELF_F_MALLOCED; - STAILQ_INSERT_TAIL(&s->s_data, d, d_next); xlate = _libelf_get_translator(elftype, ELF_TOMEMORY, elfclass); - (*xlate)(d->d_buf, e->e_rawfile + sh_offset, count, e->e_byteorder != - LIBELF_PRIVATE(byteorder)); + if (!(*xlate)(d->d_buf, d->d_size, e->e_rawfile + sh_offset, count, + e->e_byteorder != LIBELF_PRIVATE(byteorder))) { + _libelf_release_data(d); + LIBELF_SET_ERROR(DATA, 0); + return (NULL); + } + + STAILQ_INSERT_TAIL(&s->s_data, d, d_next); return (d); } diff --git a/lib/libelf/elf_scn.c b/lib/libelf/elf_scn.c index 3a9e575..2eefca1 100644 --- a/lib/libelf/elf_scn.c +++ b/lib/libelf/elf_scn.c @@ -48,7 +48,7 @@ _libelf_load_scn(Elf *e, void *ehdr) Elf32_Ehdr *eh32; Elf64_Ehdr *eh64; Elf_Scn *scn; - void (*xlator)(char *_d, char *_s, size_t _c, int _swap); + int (*xlator)(char *_d, size_t _dsz, char *_s, size_t _c, int _swap); assert(e != NULL); assert(ehdr != NULL); @@ -101,7 +101,8 @@ _libelf_load_scn(Elf *e, void *ehdr) if ((scn = _libelf_allocate_scn(e, i)) == NULL) return (0); - (*xlator)((char *) &scn->s_shdr, src, (size_t) 1, swapbytes); + (*xlator)((char *) &scn->s_shdr, sizeof(scn->s_shdr), src, + (size_t) 1, swapbytes); if (ec == ELFCLASS32) { scn->s_offset = scn->s_rawoff = diff --git a/lib/libelf/libelf_convert.m4 b/lib/libelf/libelf_convert.m4 index e3766fc..e7f6950 100644 --- a/lib/libelf/libelf_convert.m4 +++ b/lib/libelf/libelf_convert.m4 @@ -236,6 +236,7 @@ IGNORE(MOVEP) IGNORE(NOTE) define(IGNORE_BYTE, 1) /* 'lator, leave 'em bytes alone */ +define(IGNORE_GNUHASH, 1) define(IGNORE_NOTE, 1) define(IGNORE_SXWORD32, 1) define(IGNORE_XWORD32, 1) @@ -274,18 +275,18 @@ define(`SIZEDEP_OFF', 1) * `$4': ELF class specifier for types, one of [`32', `64'] */ define(`MAKEPRIM_TO_F',` -static void -libelf_cvt_$1$3_tof(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt_$1$3_tof(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { Elf$4_$2 t, *s = (Elf$4_$2 *) (uintptr_t) src; size_t c; - if (dst == src && !byteswap) - return; + (void) dsz; if (!byteswap) { (void) memcpy(dst, src, count * sizeof(*s)); - return; + return (1); } for (c = 0; c < count; c++) { @@ -293,22 +294,25 @@ libelf_cvt_$1$3_tof(char *dst, char *src, size_t count, int byteswap) SWAP_$1$3(t); WRITE_$1$3(dst,t); } + + return (1); } ') define(`MAKEPRIM_TO_M',` -static void -libelf_cvt_$1$3_tom(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt_$1$3_tom(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { Elf$4_$2 t, *d = (Elf$4_$2 *) (uintptr_t) dst; size_t c; - if (dst == src && !byteswap) - return; + if (dsz < count * sizeof(Elf$4_$2)) + return (0); if (!byteswap) { (void) memcpy(dst, src, count * sizeof(*d)); - return; + return (1); } for (c = 0; c < count; c++) { @@ -316,6 +320,8 @@ libelf_cvt_$1$3_tom(char *dst, char *src, size_t count, int byteswap) SWAP_$1$3(t); *d++ = t; } + + return (1); } ') @@ -392,12 +398,15 @@ define(`READ_STRUCT', define(`MAKE_TO_F', `ifdef(`IGNORE_'$1$3,`',` -static void -libelf_cvt$3_$1_tof(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt$3_$1_tof(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { Elf$3_$2 t, *s; size_t c; + (void) dsz; + s = (Elf$3_$2 *) (uintptr_t) src; for (c = 0; c < count; c++) { t = *s++; @@ -406,13 +415,16 @@ libelf_cvt$3_$1_tof(char *dst, char *src, size_t count, int byteswap) } WRITE_STRUCT($2,$3) } + + return (1); } ')') define(`MAKE_TO_M', `ifdef(`IGNORE_'$1$3,`',` -static void -libelf_cvt$3_$1_tom(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt$3_$1_tom(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { Elf$3_$2 t, *d; unsigned char *s,*s0; @@ -422,6 +434,9 @@ libelf_cvt$3_$1_tom(char *dst, char *src, size_t count, int byteswap) d = ((Elf$3_$2 *) (uintptr_t) dst) + (count - 1); s0 = (unsigned char *) src + (count - 1) * fsz; + if (dsz < count * sizeof(Elf$3_$2)) + return (0); + while (count--) { s = s0; READ_STRUCT($2,$3) @@ -430,6 +445,8 @@ libelf_cvt$3_$1_tom(char *dst, char *src, size_t count, int byteswap) } *d-- = t; s0 -= fsz; } + + return (1); } ')') @@ -475,12 +492,16 @@ divert(0) * simple memcpy suffices for both directions of conversion. */ -static void -libelf_cvt_BYTE_tox(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt_BYTE_tox(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { (void) byteswap; + if (dsz < count) + return (0); if (dst != src) (void) memcpy(dst, src, count); + return (1); } /* @@ -490,69 +511,81 @@ libelf_cvt_BYTE_tox(char *dst, char *src, size_t count, int byteswap) * * Argument `count' denotes the total number of bytes to be converted. */ -static void -libelf_cvt_NOTE_tom(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt_NOTE_tom(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { uint32_t namesz, descsz, type; Elf_Note *en; - size_t sz; + size_t sz, hdrsz; + + if (dsz < count) /* Destination buffer is too small. */ + return (0); - if (dst == src && !byteswap) - return; + hdrsz = 3 * sizeof(uint32_t); + if (count < hdrsz) /* Source too small. */ + return (0); if (!byteswap) { (void) memcpy(dst, src, count); - return; + return (1); } - while (count > sizeof(Elf_Note)) { - + /* Process all notes in the section. */ + while (count > hdrsz) { + /* Read the note header. */ READ_WORD(src, namesz); READ_WORD(src, descsz); READ_WORD(src, type); - if (byteswap) { - SWAP_WORD(namesz); - SWAP_WORD(descsz); - SWAP_WORD(type); - } + /* Translate. */ + SWAP_WORD(namesz); + SWAP_WORD(descsz); + SWAP_WORD(type); + /* Copy out the translated note header. */ en = (Elf_Note *) (uintptr_t) dst; en->n_namesz = namesz; en->n_descsz = descsz; en->n_type = type; + dsz -= sizeof(Elf_Note); dst += sizeof(Elf_Note); + count -= hdrsz; ROUNDUP2(namesz, 4); ROUNDUP2(descsz, 4); sz = namesz + descsz; - if (count < sz) - sz = count; + if (count < sz || dsz < sz) /* Buffers are too small. */ + return (0); (void) memcpy(dst, src, sz); src += sz; dst += sz; + count -= sz; } + + return (1); } -static void -libelf_cvt_NOTE_tof(char *dst, char *src, size_t count, int byteswap) +static int +libelf_cvt_NOTE_tof(char *dst, size_t dsz, char *src, size_t count, + int byteswap) { uint32_t namesz, descsz, type; Elf_Note *en; size_t sz; - if (dst == src && !byteswap) - return; + if (dsz < count) + return (0); if (!byteswap) { (void) memcpy(dst, src, count); - return; + return (1); } while (count > sizeof(Elf_Note)) { @@ -562,12 +595,9 @@ libelf_cvt_NOTE_tof(char *dst, char *src, size_t count, int byteswap) descsz = en->n_descsz; type = en->n_type; - if (byteswap) { - SWAP_WORD(namesz); - SWAP_WORD(descsz); - SWAP_WORD(type); - } - + SWAP_WORD(namesz); + SWAP_WORD(descsz); + SWAP_WORD(type); WRITE_WORD(dst, namesz); WRITE_WORD(dst, descsz); @@ -589,15 +619,21 @@ libelf_cvt_NOTE_tof(char *dst, char *src, size_t count, int byteswap) dst += sz; count -= sz; } + + return (1); } MAKE_TYPE_CONVERTERS(ELF_TYPE_LIST) struct converters { - void (*tof32)(char *dst, char *src, size_t cnt, int byteswap); - void (*tom32)(char *dst, char *src, size_t cnt, int byteswap); - void (*tof64)(char *dst, char *src, size_t cnt, int byteswap); - void (*tom64)(char *dst, char *src, size_t cnt, int byteswap); + int (*tof32)(char *dst, size_t dsz, char *src, size_t cnt, + int byteswap); + int (*tom32)(char *dst, size_t dsz, char *src, size_t cnt, + int byteswap); + int (*tof64)(char *dst, size_t dsz, char *src, size_t cnt, + int byteswap); + int (*tom64)(char *dst, size_t dsz, char *src, size_t cnt, + int byteswap); }; divert(-1) @@ -647,8 +683,8 @@ CONVERTER_NAMES(ELF_TYPE_LIST) } }; -void (*_libelf_get_translator(Elf_Type t, int direction, int elfclass)) - (char *_dst, char *_src, size_t _cnt, int _byteswap) +int (*_libelf_get_translator(Elf_Type t, int direction, int elfclass)) + (char *_dst, size_t dsz, char *_src, size_t _cnt, int _byteswap) { assert(elfclass == ELFCLASS32 || elfclass == ELFCLASS64); assert(direction == ELF_TOFILE || direction == ELF_TOMEMORY); diff --git a/lib/libelf/libelf_ehdr.c b/lib/libelf/libelf_ehdr.c index e0c4886..3b83f2c 100644 --- a/lib/libelf/libelf_ehdr.c +++ b/lib/libelf/libelf_ehdr.c @@ -46,7 +46,7 @@ _libelf_load_extended(Elf *e, int ec, uint64_t shoff, uint16_t phnum, { Elf_Scn *scn; size_t fsz; - void (*xlator)(char *_d, char *_s, size_t _c, int _swap); + int (*xlator)(char *_d, size_t _dsz, char *_s, size_t _c, int _swap); uint32_t shtype; assert(STAILQ_EMPTY(&e->e_u.e_elf.e_scn)); @@ -63,7 +63,8 @@ _libelf_load_extended(Elf *e, int ec, uint64_t shoff, uint16_t phnum, return (0); xlator = _libelf_get_translator(ELF_T_SHDR, ELF_TOMEMORY, ec); - (*xlator)((char *) &scn->s_shdr, e->e_rawfile + shoff, (size_t) 1, + (*xlator)((char *) &scn->s_shdr, sizeof(scn->s_shdr), + e->e_rawfile + shoff, (size_t) 1, e->e_byteorder != LIBELF_PRIVATE(byteorder)); #define GET_SHDR_MEMBER(M) ((ec == ELFCLASS32) ? scn->s_shdr.s_shdr32.M : \ @@ -105,7 +106,7 @@ _libelf_ehdr(Elf *e, int ec, int allocate) size_t fsz, msz; uint16_t phnum, shnum, strndx; uint64_t shoff; - void (*xlator)(char *_d, char *_s, size_t _c, int _swap); + int (*xlator)(char *_d, size_t _dsz, char *_s, size_t _c, int _swap); assert(ec == ELFCLASS32 || ec == ELFCLASS64); @@ -167,7 +168,7 @@ _libelf_ehdr(Elf *e, int ec, int allocate) return (ehdr); xlator = _libelf_get_translator(ELF_T_EHDR, ELF_TOMEMORY, ec); - (*xlator)(ehdr, e->e_rawfile, (size_t) 1, + (*xlator)(ehdr, msz, e->e_rawfile, (size_t) 1, e->e_byteorder != LIBELF_PRIVATE(byteorder)); /* diff --git a/lib/libelf/libelf_phdr.c b/lib/libelf/libelf_phdr.c index fd43851..ed3f650 100644 --- a/lib/libelf/libelf_phdr.c +++ b/lib/libelf/libelf_phdr.c @@ -45,7 +45,7 @@ _libelf_getphdr(Elf *e, int ec) Elf32_Ehdr *eh32; Elf64_Ehdr *eh64; void *ehdr, *phdr; - void (*xlator)(char *_d, char *_s, size_t _c, int _swap); + int (*xlator)(char *_d, size_t _dsz, char *_s, size_t _c, int _swap); assert(ec == ELFCLASS32 || ec == ELFCLASS64); @@ -103,7 +103,7 @@ _libelf_getphdr(Elf *e, int ec) xlator = _libelf_get_translator(ELF_T_PHDR, ELF_TOMEMORY, ec); - (*xlator)(phdr, e->e_rawfile + phoff, phnum, + (*xlator)(phdr, phnum * msz, e->e_rawfile + phoff, phnum, e->e_byteorder != LIBELF_PRIVATE(byteorder)); return (phdr); diff --git a/lib/libelf/libelf_xlate.c b/lib/libelf/libelf_xlate.c index 362e82d..5137b2c 100644 --- a/lib/libelf/libelf_xlate.c +++ b/lib/libelf/libelf_xlate.c @@ -48,6 +48,7 @@ Elf_Data * _libelf_xlate(Elf_Data *dst, const Elf_Data *src, unsigned int encoding, int elfclass, int direction) { + int byteswap; size_t cnt, dsz, fsz, msz; uintptr_t sb, se, db, de; @@ -132,12 +133,17 @@ _libelf_xlate(Elf_Data *dst, const Elf_Data *src, unsigned int encoding, dst->d_type = src->d_type; dst->d_size = dsz; + byteswap = encoding != LIBELF_PRIVATE(byteorder); + if (src->d_size == 0 || - (db == sb && encoding == LIBELF_PRIVATE(byteorder) && fsz == msz)) + (db == sb && !byteswap && fsz == msz)) return (dst); /* nothing more to do */ - (_libelf_get_translator(src->d_type, direction, elfclass))(dst->d_buf, - src->d_buf, cnt, encoding != LIBELF_PRIVATE(byteorder)); + if (!(_libelf_get_translator(src->d_type, direction, elfclass)) + (dst->d_buf, dsz, src->d_buf, cnt, byteswap)) { + LIBELF_SET_ERROR(DATA, 0); + return (NULL); + } return (dst); } |