This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
Re: [PATCH] Fix section corruption bug
- From: Thilo Schulz <thilo at tjps dot eu>
- To: elfutils-devel at lists dot fedorahosted dot org
- Date: Tue, 10 Jun 2014 15:31:09 +0200
- Subject: Re: [PATCH] Fix section corruption bug
On Tuesday 10 June 2014 11:48:15 Mark Wielaard wrote:
> On Mon, 2014-06-09 at 21:05 +0200, Thilo Schulz wrote:
> > When adding data to existing sections in ELF files, libelf may corrupt
> > those sections, i.e. overwrite the existing data if certain conditions
> > are met.
> >
> > If an Elf_Scn structure has seen a call to elf_rawdata(scn) before but no
> > call to elf_getdata(scn), scn->read_data flag is set, but not
> > scn->data_list_rear.
>
> Do you happen to have a small testcase that shows the buggy behavior?
Sure. This is an excerpt from the final program. A short word on what it is
supposed to do in my practical application:
I am doing a project for the AVR platform, which is mixed C/assembly. For
command parser functions that are called out of assembly into C code, I need
to replace the final return statements with rjmps back to
program code in an object file generated from my assembly.
So I want to add new symbols and relocations to a cmd.o file.
Find as attachment a simple program, which checks for the presence of the two
symbols cmd_response and cmd_noresponse and adds them if they don't exist.
Since the AVR architecture is 8 bit, I am only using Elf32_ structures
throughout, so you may need to compile test .o objects with -m32 for the test
program to work on them.
> I was wondering whether we want to check scn->rawdata.s directly, or if
> we could rely on ELF_F_FILEDATA being set for scn->flags?
Seems reasonable though I don't know the code as well as you do I guess.
As a further note: A similar bug, albeit for slightly different reasons, occurs
when adding relocations. Adding a relocation with elf_newdata() then
elf_update()
results in the old data being "forgotten" if there was no elf_getdata() call
before to load that data into memory. The cause is a bit different because in
this case, there was not a call to elf_rawdata() before and this still
happened. I imagine, this might also be a problem for string tables.
--
Best regards,
Thilo Schulz
/*
===========================================================================
Copyright (C) 2014 Essential Nature (Thilo Schulz)
Fix the command parse functions so as to replace ret
with an rjmp to the appropriate location
===========================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sysexits.h>
#include <string.h>
#include <errno.h>
#include <libelf.h>
#include <gelf.h>
int isbe = 0;
int found_resp, found_noresp;
Elf32_Sym fresp, fnoresp;
void do_help(char *progname)
{
printf("Usage: %s [options] <filename>\n"
"Modify the elf files for the command parser so as to use rjmp instead\n"
"of ret instruction to return to main interrupt service routine.\n\n"
"Options:\n"
" -h Print this help\n"
"\n",
progname);
}
Elf_Scn *get_section(Elf *obj, Elf32_Word sh_type, const char *secname)
{
Elf_Scn *cursec;
Elf32_Shdr *curhdr;
size_t shstrndex;
char *name;
if(elf_getshdrstrndx(obj, &shstrndex))
{
fprintf(stderr, "Couldn't get list of section names: %s\n", elf_errmsg(-1));
exit(EX_DATAERR);
}
cursec = NULL;
while((cursec = elf_nextscn(obj, cursec)))
{
if(!(curhdr = elf32_getshdr(cursec)))
{
fprintf(stderr, "Couldn't get section header: %s\n", elf_errmsg(-1));
exit(EX_DATAERR);
}
if(name = elf_strptr(obj, shstrndex, curhdr->sh_name))
{
if(curhdr->sh_type == sh_type && (!secname || !strcmp(name, secname)))
return cursec;
}
}
return NULL;
}
size_t add_stringtotable(Elf *obj, size_t tableind, char *str)
{
Elf_Scn *strsec;
Elf32_Shdr *strhdr;
Elf_Data *data;
size_t lastofs;
if(!(strsec = elf_getscn(obj, tableind)))
{
fprintf(stderr, "Couldn't get section to string table %zd: %s\n", elf_errmsg(-1));
exit(EX_DATAERR);
}
if(!(strhdr = elf32_getshdr(strsec)))
{
fprintf(stderr, "Couldn't get header of string table\n");
exit(EX_DATAERR);
}
// Workaround for bug in libelf
// data = elf_getdata(strsec, NULL);
if(!(data = elf_newdata(strsec)))
{
fprintf(stderr, "Couldn't add data to string table: %s\n", elf_errmsg(-1));
exit(EX_DATAERR);
}
data->d_align = 1;
data->d_buf = str;
data->d_size = strlen(str) + 1;
data->d_type = ELF_T_BYTE;
lastofs = strhdr->sh_size;
strhdr->sh_size += data->d_size;
return lastofs;
}
void add_undefinedsym(Elf *obj, Elf_Scn *scn, Elf32_Sym *dest, size_t tableind, char *str)
{
Elf_Data *data;
GElf_Sym undef;
if(!(data = elf_newdata(scn)))
{
fprintf(stderr, "Couldn't add symbol %s to symbol table: %s\n", str, elf_errmsg(-1));
exit(EX_DATAERR);
}
undef.st_name = add_stringtotable(obj, tableind, str);
undef.st_value = 0;
undef.st_size = 0;
undef.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
undef.st_other = STV_DEFAULT;
undef.st_shndx = 0;
data->d_align = 4;
data->d_buf = dest;
data->d_size = sizeof(*dest);
data->d_type = ELF_T_SYM;
if(!gelf_update_sym(data, 0, &undef))
{
fprintf(stderr, "Couldn't add symbol %s to symbol table: %s\n", str, elf_errmsg(-1));
exit(EX_DATAERR);
}
}
int modify_elf(const char *fname)
{
int fd;
Elf *obj;
Elf32_Ehdr *ehdr;
Elf_Scn *symsec, *progsec;
Elf32_Shdr *symsechdr;
Elf_Data *data, *progdata;
GElf_Sym sym;
char *name;
int numsym, index;
if(elf_version(EV_CURRENT) == EV_NONE)
{
fprintf(stderr, "Could not initialize ELF library: %s\n", elf_errmsg(-1));
return EX_SOFTWARE;
}
if((fd = open(fname, O_RDWR, 0)) < 0)
{
fprintf(stderr, "Couldn't open elf file: %s\n", strerror(errno));
return EX_NOINPUT;
}
if(!(obj = elf_begin(fd, ELF_C_RDWR, NULL)))
{
fprintf(stderr, "Couldn't parse elf file: %s\n", elf_errmsg(-1));
return EX_DATAERR;
}
if(!(ehdr = elf32_getehdr(obj)))
{
fprintf(stderr, "Failed to retrieve elf header: %s\n", elf_errmsg(-1));
return EX_DATAERR;
}
if((ehdr->e_ident[EI_DATA] & ELFDATA2MSB))
isbe = 1;
symsec = get_section(obj, SHT_SYMTAB, ".symtab");
if(!symsec)
{
fprintf(stderr, "No symbol section found\n");
return EX_DATAERR;
}
data = NULL;
if(!(data = elf_getdata(symsec, data)) || !data->d_size)
{
fprintf(stderr, "No data associated with symbol section\n");
return EX_DATAERR;
}
if(!(symsechdr = elf32_getshdr(symsec)))
{
fprintf(stderr, "Couldn't get header of symbol section\n");
return EX_DATAERR;
}
numsym = 0;
found_resp = 0;
found_noresp = 0;
while(gelf_getsym(data, numsym, &sym))
{
name = elf_strptr(obj, symsechdr->sh_link, sym.st_name);
if(!strcmp(name, "cmd_response"))
found_resp = numsym;
else if(!strcmp(name, "cmd_noresponse"))
found_noresp = numsym;
numsym++;
}
/*
* Make sure symbol table contains undefined symbols cmd_response and cmd_noresponse
*/
if(!found_noresp)
{
add_undefinedsym(obj, symsec, &fnoresp, symsechdr->sh_link, "cmd_noresponse");
found_noresp = numsym++;
}
if(!found_resp)
{
add_undefinedsym(obj, symsec, &fresp, symsechdr->sh_link, "cmd_response");
found_resp = numsym++;
}
elf_update(obj, ELF_C_WRITE);
elf_end(obj);
if(close(fd) < 0)
{
fprintf(stderr, "Couldn't close elf file: %s\n", strerror(errno));
return EX_NOINPUT;
}
}
int main(int argc, char **argv)
{
int opt;
if(argc < 2)
{
if(argc < 1)
do_help("elf-fixcmd");
else
do_help(argv[0]);
return EX_OK;
}
while((opt = getopt(argc, argv, "h")) != -1)
{
switch(opt)
{
case 'h':
do_help(argv[0]);
return EX_OK;
break;
default:
return EX_USAGE;
}
}
return modify_elf(argv[optind]);
}