Commit baa41469 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar

objtool: Implement stack validation 2.0

This is a major rewrite of objtool.  Instead of only tracking frame
pointer changes, it now tracks all stack-related operations, including
all register saves/restores.

In addition to making stack validation more robust, this also paves the
way for undwarf generation.
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/678bd94c0566c6129bcc376cddb259c4c5633004.1498659915.git.jpoimboe@redhat.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent c207aee4
......@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
all: $(OBJTOOL)
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES)
LDFLAGS += -lelf $(LIBSUBCMD)
# Allow old libelf to be used:
......
......@@ -19,25 +19,63 @@
#define _ARCH_H
#include <stdbool.h>
#include <linux/list.h>
#include "elf.h"
#include "cfi.h"
#define INSN_FP_SAVE 1
#define INSN_FP_SETUP 2
#define INSN_FP_RESTORE 3
#define INSN_JUMP_CONDITIONAL 4
#define INSN_JUMP_UNCONDITIONAL 5
#define INSN_JUMP_DYNAMIC 6
#define INSN_CALL 7
#define INSN_CALL_DYNAMIC 8
#define INSN_RETURN 9
#define INSN_CONTEXT_SWITCH 10
#define INSN_NOP 11
#define INSN_OTHER 12
#define INSN_JUMP_CONDITIONAL 1
#define INSN_JUMP_UNCONDITIONAL 2
#define INSN_JUMP_DYNAMIC 3
#define INSN_CALL 4
#define INSN_CALL_DYNAMIC 5
#define INSN_RETURN 6
#define INSN_CONTEXT_SWITCH 7
#define INSN_STACK 8
#define INSN_NOP 9
#define INSN_OTHER 10
#define INSN_LAST INSN_OTHER
enum op_dest_type {
OP_DEST_REG,
OP_DEST_REG_INDIRECT,
OP_DEST_MEM,
OP_DEST_PUSH,
OP_DEST_LEAVE,
};
struct op_dest {
enum op_dest_type type;
unsigned char reg;
int offset;
};
enum op_src_type {
OP_SRC_REG,
OP_SRC_REG_INDIRECT,
OP_SRC_CONST,
OP_SRC_POP,
OP_SRC_ADD,
OP_SRC_AND,
};
struct op_src {
enum op_src_type type;
unsigned char reg;
int offset;
};
struct stack_op {
struct op_dest dest;
struct op_src src;
};
void arch_initial_func_cfi_state(struct cfi_state *state);
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
unsigned int *len, unsigned char *type,
unsigned long *displacement);
unsigned long *immediate, struct stack_op *op);
bool arch_callee_saved_reg(unsigned char reg);
#endif /* _ARCH_H */
This diff is collapsed.
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
*
* 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
* of the License, 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _OBJTOOL_CFI_H
#define _OBJTOOL_CFI_H
#define CFI_UNDEFINED -1
#define CFI_CFA -2
#define CFI_SP_INDIRECT -3
#define CFI_BP_INDIRECT -4
#define CFI_AX 0
#define CFI_DX 1
#define CFI_CX 2
#define CFI_BX 3
#define CFI_SI 4
#define CFI_DI 5
#define CFI_BP 6
#define CFI_SP 7
#define CFI_R8 8
#define CFI_R9 9
#define CFI_R10 10
#define CFI_R11 11
#define CFI_R12 12
#define CFI_R13 13
#define CFI_R14 14
#define CFI_R15 15
#define CFI_RA 16
#define CFI_NUM_REGS 17
struct cfi_reg {
int base;
int offset;
};
struct cfi_state {
struct cfi_reg cfa;
struct cfi_reg regs[CFI_NUM_REGS];
};
#endif /* _OBJTOOL_CFI_H */
This diff is collapsed.
......@@ -20,22 +20,34 @@
#include <stdbool.h>
#include "elf.h"
#include "cfi.h"
#include "arch.h"
#include <linux/hashtable.h>
struct insn_state {
struct cfi_reg cfa;
struct cfi_reg regs[CFI_NUM_REGS];
int stack_size;
bool bp_scratch;
bool drap;
int drap_reg;
};
struct instruction {
struct list_head list;
struct hlist_node hash;
struct section *sec;
unsigned long offset;
unsigned int len, state;
unsigned int len;
unsigned char type;
unsigned long immediate;
bool alt_group, visited, dead_end;
bool alt_group, visited, dead_end, ignore;
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
struct symbol *func;
struct stack_op stack_op;
struct insn_state state;
};
struct objtool_file {
......@@ -48,4 +60,7 @@ struct objtool_file {
int check(const char *objname, bool nofp);
#define for_each_insn(file, insn) \
list_for_each_entry(insn, &file->insn_list, list)
#endif /* _CHECK_H */
......@@ -37,6 +37,9 @@
#define ELF_C_READ_MMAP ELF_C_READ
#endif
#define WARN_ELF(format, ...) \
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
struct section *find_section_by_name(struct elf *elf, const char *name)
{
struct section *sec;
......@@ -139,12 +142,12 @@ static int read_sections(struct elf *elf)
int i;
if (elf_getshdrnum(elf->elf, &sections_nr)) {
perror("elf_getshdrnum");
WARN_ELF("elf_getshdrnum");
return -1;
}
if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
perror("elf_getshdrstrndx");
WARN_ELF("elf_getshdrstrndx");
return -1;
}
......@@ -165,37 +168,36 @@ static int read_sections(struct elf *elf)
s = elf_getscn(elf->elf, i);
if (!s) {
perror("elf_getscn");
WARN_ELF("elf_getscn");
return -1;
}
sec->idx = elf_ndxscn(s);
if (!gelf_getshdr(s, &sec->sh)) {
perror("gelf_getshdr");
WARN_ELF("gelf_getshdr");
return -1;
}
sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
if (!sec->name) {
perror("elf_strptr");
WARN_ELF("elf_strptr");
return -1;
}
sec->elf_data = elf_getdata(s, NULL);
if (!sec->elf_data) {
perror("elf_getdata");
sec->data = elf_getdata(s, NULL);
if (!sec->data) {
WARN_ELF("elf_getdata");
return -1;
}
if (sec->elf_data->d_off != 0 ||
sec->elf_data->d_size != sec->sh.sh_size) {
if (sec->data->d_off != 0 ||
sec->data->d_size != sec->sh.sh_size) {
WARN("unexpected data attributes for %s", sec->name);
return -1;
}
sec->data = (unsigned long)sec->elf_data->d_buf;
sec->len = sec->elf_data->d_size;
sec->len = sec->data->d_size;
}
/* sanity check, one more call to elf_nextscn() should return NULL */
......@@ -232,15 +234,15 @@ static int read_symbols(struct elf *elf)
sym->idx = i;
if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) {
perror("gelf_getsym");
if (!gelf_getsym(symtab->data, i, &sym->sym)) {
WARN_ELF("gelf_getsym");
goto err;
}
sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
sym->sym.st_name);
if (!sym->name) {
perror("elf_strptr");
WARN_ELF("elf_strptr");
goto err;
}
......@@ -322,8 +324,8 @@ static int read_relas(struct elf *elf)
}
memset(rela, 0, sizeof(*rela));
if (!gelf_getrela(sec->elf_data, i, &rela->rela)) {
perror("gelf_getrela");
if (!gelf_getrela(sec->data, i, &rela->rela)) {
WARN_ELF("gelf_getrela");
return -1;
}
......@@ -362,12 +364,6 @@ struct elf *elf_open(const char *name)
INIT_LIST_HEAD(&elf->sections);
elf->name = strdup(name);
if (!elf->name) {
perror("strdup");
goto err;
}
elf->fd = open(name, O_RDONLY);
if (elf->fd == -1) {
perror("open");
......@@ -376,12 +372,12 @@ struct elf *elf_open(const char *name)
elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL);
if (!elf->elf) {
perror("elf_begin");
WARN_ELF("elf_begin");
goto err;
}
if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
perror("gelf_getehdr");
WARN_ELF("gelf_getehdr");
goto err;
}
......@@ -407,6 +403,12 @@ void elf_close(struct elf *elf)
struct symbol *sym, *tmpsym;
struct rela *rela, *tmprela;
if (elf->elf)
elf_end(elf->elf);
if (elf->fd > 0)
close(elf->fd);
list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
list_del(&sym->list);
......@@ -421,11 +423,6 @@ void elf_close(struct elf *elf)
list_del(&sec->list);
free(sec);
}
if (elf->name)
free(elf->name);
if (elf->fd > 0)
close(elf->fd);
if (elf->elf)
elf_end(elf->elf);
free(elf);
}
......@@ -37,10 +37,9 @@ struct section {
DECLARE_HASHTABLE(rela_hash, 16);
struct section *base, *rela;
struct symbol *sym;
Elf_Data *elf_data;
Elf_Data *data;
char *name;
int idx;
unsigned long data;
unsigned int len;
};
......@@ -86,6 +85,7 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
struct symbol *find_containing_func(struct section *sec, unsigned long offset);
void elf_close(struct elf *elf);
#define for_each_sec(file, sec) \
list_for_each_entry(sec, &file->elf->sections, list)
#endif /* _OBJTOOL_ELF_H */
......@@ -91,16 +91,16 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
alt->jump_or_nop = entry->jump_or_nop;
if (alt->group) {
alt->orig_len = *(unsigned char *)(sec->data + offset +
alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +
entry->orig_len);
alt->new_len = *(unsigned char *)(sec->data + offset +
alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
entry->new_len);
}
if (entry->feature) {
unsigned short feature;
feature = *(unsigned short *)(sec->data + offset +
feature = *(unsigned short *)(sec->data->d_buf + offset +
entry->feature);
/*
......
......@@ -18,6 +18,13 @@
#ifndef _WARN_H
#define _WARN_H
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "elf.h"
extern const char *objname;
static inline char *offstr(struct section *sec, unsigned long offset)
......@@ -57,4 +64,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
free(_str); \
})
#define WARN_ELF(format, ...) \
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
#endif /* _WARN_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment