diff options
Diffstat (limited to 'trunk/src/execstack.c')
-rw-r--r-- | trunk/src/execstack.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/trunk/src/execstack.c b/trunk/src/execstack.c new file mode 100644 index 0000000..f38fead --- /dev/null +++ b/trunk/src/execstack.c @@ -0,0 +1,459 @@ +/* Copyright (C) 2003, 2005 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "prelink.h" + +int set; +int execflag; + +const char *argp_program_version = "execstack 1.0 (20061201) Wind River"; + +const char *argp_program_bug_address = "<support@windriver.com>"; + +static char argp_doc[] = "execstack -- program to query or set executable stack flag"; + +static struct argp_option options[] = { + {"set-execstack", 's', 0, 0, "Set executable stack flag bit" }, + {"execstack", 's', 0, OPTION_HIDDEN, "" }, + {"clear-execstack", 'c', 0, 0, "Clear executable stack flag bit" }, + {"noexecstack", 'c', 0, OPTION_HIDDEN, "" }, + {"query", 'q', 0, 0, "Query executable stack flag bit" }, + { 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 's': + set = 1; + execflag = 1; + break; + case 'c': + set = 1; + execflag = 0; + break; + case 'q': + set = 0; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, 0, argp_doc }; + +static int execstack_set (DSO *dso, int flag); + +static void +execstack_fill_phdr (DSO *dso, int i, int flag) +{ + memset (&dso->phdr[i], 0, sizeof (dso->phdr[i])); + dso->phdr[i].p_type = PT_GNU_STACK; + dso->phdr[i].p_flags = PF_W | PF_R | (flag ? PF_X : 0); + dso->phdr[i].p_align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); +} + +static int +execstack_make_rdwr (DSO *dso, int flag) +{ + int i, fd = -1, status; + pid_t pid; + DSO *ndso = NULL; + char *p = NULL; + char filename[strlen (dso->filename) + sizeof ".#execstack#.XXXXXX"]; + + for (i = 0; i < dso->ehdr.e_shnum; ++i) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + if (strcmp (name, ".gnu.prelink_undo") == 0) + break; + } + + if (i == dso->ehdr.e_shnum) + return reopen_dso (dso, NULL, NULL) ? 1 : -1; + + /* We need to unprelink the file first, so that prelink --undo + or reprelinking it doesn't destroy the PT_GNU_STACK segment + header we've created. */ + sprintf (filename, "%s.#execstack#.XXXXXX", dso->filename); + + fd = wrap_mkstemp (filename); + if (fd == -1) + { + error (0, 0, "%s: Cannot create temporary file", + dso->filename); + goto error_out; + } + + p = strdup (dso->filename); + if (p == NULL) + { + error (0, ENOMEM, "%s: Cannot create temporary file", + dso->filename); + goto error_out; + } + + pid = vfork (); + if (pid == 0) + { + close (fd); + execlp ("prelink", "prelink", "-u", "-o", filename, + dso->filename, NULL); + execl (SBINDIR "/prelink", "prelink", "-u", "-o", filename, + dso->filename, NULL); + _exit (-1); + } + + if (pid < 0) + { + error (0, errno, "%s: Cannot run prelink --undo", + dso->filename); + goto error_out; + } + + if (waitpid (pid, &status, 0) < 0 + || !WIFEXITED (status) + || WEXITSTATUS (status)) + { + error (0, 0, "%s: prelink --undo failed", dso->filename); + goto error_out; + } + + ndso = open_dso (filename); + if (ndso == NULL) + { + error (0, 0, "%s: Couldn't open prelink --undo output", + dso->filename); + goto error_out; + } + + for (i = 0; i < ndso->ehdr.e_shnum; ++i) + { + const char *name = strptr (ndso, ndso->ehdr.e_shstrndx, + ndso->shdr[i].sh_name); + if (strcmp (name, ".gnu.prelink_undo") == 0) + break; + } + + if (i != ndso->ehdr.e_shnum) + { + error (0, 0, "%s: prelink --undo output contains .gnu.prelink_undo section", + dso->filename); + goto error_out; + } + + if (ndso->ehdr.e_type != dso->ehdr.e_type) + { + error (0, 0, "%s: Object type changed during prelink --undo operation", + dso->filename); + } + + if (ndso->filename != ndso->soname) + free ((char *) ndso->filename); + ndso->filename = p; + p = NULL; + + wrap_unlink (filename); + close (fd); + fd = -1; + close_dso (dso); + return execstack_set (ndso, flag); + +error_out: + free (p); + if (ndso != NULL) + close_dso (ndso); + if (fd != -1) + { + wrap_unlink (filename); + close (fd); + } + close_dso (dso); + return 1; +} + +static int +execstack_set (DSO *dso, int flag) +{ + int i, null = -1, last, ret; + GElf_Addr lowoff = ~(GElf_Addr) 0, start = 0, align = 0; + GElf_Addr adjust; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_GNU_STACK) + { + /* Found PT_GNU_STACK. Check if we need any change or not. */ + if (flag ^ ((dso->phdr[i].p_flags & PF_X) != 0)) + { + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + dso->phdr[i].p_flags ^= PF_X; + goto out_write; + } + else + goto out_close; + } + else if (dso->phdr[i].p_type == PT_NULL) + null = i; + + if (null != -1) + { + /* Overwrite PT_NULL segment with PT_GNU_STACK. */ + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + execstack_fill_phdr (dso, i, flag); + goto out_write; + } + + if (dso->ehdr.e_shnum == 0) + { + error (0, 0, "%s: Section header table missing", dso->filename); + goto error_out; + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + if (lowoff > dso->shdr[i].sh_offset) + { + if (dso->shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + { + lowoff = dso->shdr[i].sh_offset; + start = dso->shdr[i].sh_addr; + } + else + { + error (0, 0, "%s: Non-alloced sections before alloced ones", + dso->filename); + goto error_out; + } + } + + if (dso->shdr[i].sh_addralign > align) + align = dso->shdr[i].sh_addralign; + } + + if (dso->ehdr.e_phoff >= lowoff) + { + error (0, 0, "%s: Program header table not before all sections", + dso->filename); + goto error_out; + } + + if (dso->ehdr.e_shoff <= lowoff) + { + error (0, 0, "%s: Section header table before first section", + dso->filename); + goto error_out; + } + + if (dso->ehdr.e_phoff + (dso->ehdr.e_phnum + 1) * dso->ehdr.e_phentsize + <= lowoff) + { + /* There is enough space for the headers even without reshuffling + anything. */ + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_PHDR) + { + if (dso->phdr[i].p_filesz + == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_filesz += dso->ehdr.e_phentsize; + if (dso->phdr[i].p_memsz + == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_memsz += dso->ehdr.e_phentsize; + } + i = dso->ehdr.e_phnum++; + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + execstack_fill_phdr (dso, i, flag); + goto out_write; + } + + if (dso->ehdr.e_type != ET_DYN) + { + error (0, 0, "%s: Reshuffling of objects to make room for\n" + "program header entry only supported for shared libraries", + dso->filename); + goto error_out; + } + + adjust = dso->ehdr.e_phoff + (dso->ehdr.e_phnum + 1) * dso->ehdr.e_phentsize + - lowoff; + if (align) + adjust = (adjust + align - 1) & ~(align - 1); + + /* Need to make sure adjust doesn't cause different Phdr segments + to overlap on the same page. */ + last = -1; + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz >= start) + { + if (last != -1 + && (((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz - 1) + ^ dso->phdr[i].p_vaddr) + & ~(dso->arch->max_page_size - 1)) + && !(((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz + + adjust - 1) + ^ (dso->phdr[i].p_vaddr + adjust)) + & ~(dso->arch->max_page_size - 1))) + { + if (align >= dso->arch->max_page_size) + { + error (0, 0, "%s: Cannot grow reloc sections", dso->filename); + goto error_out; + } + adjust = (adjust + dso->arch->max_page_size - 1) + & ~(dso->arch->max_page_size - 1); + } + last = i; + } + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_PHDR) + { + if (dso->phdr[i].p_filesz == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_filesz += dso->ehdr.e_phentsize; + if (dso->phdr[i].p_memsz == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_memsz += dso->ehdr.e_phentsize; + } + + i = dso->ehdr.e_phnum++; + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + + if (adjust_dso (dso, start, adjust)) + goto error_out; + + execstack_fill_phdr (dso, i, flag); + +out_write: + if (dynamic_info_is_set (dso, DT_CHECKSUM_BIT) + && dso_is_rdwr (dso) + && prelink_set_checksum (dso)) + goto error_out; + + dso->permissive = 1; + + return update_dso (dso, NULL); + +out_close: + close_dso (dso); + return 0; + +error_out: + close_dso (dso); + return 1; +} + +static int +execstack_query (DSO *dso) +{ + int stack = '?', i; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_GNU_STACK) + { + stack = (dso->phdr[i].p_flags & PF_X) ? 'X' : '-'; + break; + } + printf ("%c %s\n", stack, dso->filename); + close_dso (dso); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int remaining, failures = 0; + + setlocale (LC_ALL, ""); + + argp_parse (&argp, argc, argv, 0, &remaining, 0); + + elf_version (EV_CURRENT); + + if (remaining == argc) + error (EXIT_FAILURE, 0, "no files given"); + + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + int ret; + + if (dso == NULL) + { + ++failures; + continue; + } + + if (dso->ehdr.e_type != ET_DYN + && dso->ehdr.e_type != ET_EXEC) + { + ++failures; + error (0, 0, "%s is not a shared library nor executable", dso->filename); + continue; + } + + if (set) + ret = execstack_set (dso, execflag); + else + ret = execstack_query (dso); + + if (ret) + ++failures; + } + + return failures; +} + +/* FIXME: Dummy. When arch dependent files are split into adjust and prelink + parts, this can go away. */ +struct prelink_conflict * +prelink_conflict (struct prelink_info *info, GElf_Word r_sym, int reloc_type) +{ + abort (); +} + +GElf_Rela * +prelink_conflict_add_rela (struct prelink_info *info) +{ + abort (); +} + +GElf_Addr mmap_reg_start; +GElf_Addr mmap_reg_end; +int exec_shield; |