Commit 4bc06c59 authored by Michael Ellerman's avatar Michael Ellerman

Merge branch 'topic/func-desc-lkdtm' into next

Merge a topic branch we are maintaining with some cross-architecture
changes to function descriptor handling and their use in LKDTM.

From Christophe's cover letter:

Fix LKDTM for PPC64/IA64/PARISC

PPC64/IA64/PARISC have function descriptors. LKDTM doesn't work on those
three architectures because LKDTM messes up function descriptors with
functions.

This series does some cleanup in the three architectures and refactors
function descriptors so that it can then easily use it in a generic way
in LKDTM.
parents 8219d31e 5e5a6c54
...@@ -205,6 +205,9 @@ config HAVE_FUNCTION_ERROR_INJECTION ...@@ -205,6 +205,9 @@ config HAVE_FUNCTION_ERROR_INJECTION
config HAVE_NMI config HAVE_NMI
bool bool
config HAVE_FUNCTION_DESCRIPTORS
bool
config TRACE_IRQFLAGS_SUPPORT config TRACE_IRQFLAGS_SUPPORT
bool bool
......
...@@ -35,6 +35,7 @@ config IA64 ...@@ -35,6 +35,7 @@ config IA64
select HAVE_SETUP_PER_CPU_AREA select HAVE_SETUP_PER_CPU_AREA
select TTY select TTY
select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRACEHOOK
select HAVE_FUNCTION_DESCRIPTORS
select HAVE_VIRT_CPU_ACCOUNTING select HAVE_VIRT_CPU_ACCOUNTING
select HUGETLB_PAGE_SIZE_VARIABLE if HUGETLB_PAGE select HUGETLB_PAGE_SIZE_VARIABLE if HUGETLB_PAGE
select VIRT_TO_BUS select VIRT_TO_BUS
......
...@@ -226,7 +226,7 @@ struct got_entry { ...@@ -226,7 +226,7 @@ struct got_entry {
* Layout of the Function Descriptor * Layout of the Function Descriptor
*/ */
struct fdesc { struct fdesc {
uint64_t ip; uint64_t addr;
uint64_t gp; uint64_t gp;
}; };
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
typedef struct fdesc func_desc_t;
#include <asm-generic/sections.h> #include <asm-generic/sections.h>
extern char __phys_per_cpu_start[]; extern char __phys_per_cpu_start[];
...@@ -27,25 +30,4 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b ...@@ -27,25 +30,4 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
extern char __start_unwind[], __end_unwind[]; extern char __start_unwind[], __end_unwind[];
extern char __start_ivt_text[], __end_ivt_text[]; extern char __start_ivt_text[], __end_ivt_text[];
#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
#undef dereference_function_descriptor
static inline void *dereference_function_descriptor(void *ptr)
{
struct fdesc *desc = ptr;
void *p;
if (!get_kernel_nofault(p, (void *)&desc->ip))
ptr = p;
return ptr;
}
#undef dereference_kernel_function_descriptor
static inline void *dereference_kernel_function_descriptor(void *ptr)
{
if (ptr < (void *)__start_opd || ptr >= (void *)__end_opd)
return ptr;
return dereference_function_descriptor(ptr);
}
#endif /* _ASM_IA64_SECTIONS_H */ #endif /* _ASM_IA64_SECTIONS_H */
...@@ -602,15 +602,15 @@ get_fdesc (struct module *mod, uint64_t value, int *okp) ...@@ -602,15 +602,15 @@ get_fdesc (struct module *mod, uint64_t value, int *okp)
return value; return value;
/* Look for existing function descriptor. */ /* Look for existing function descriptor. */
while (fdesc->ip) { while (fdesc->addr) {
if (fdesc->ip == value) if (fdesc->addr == value)
return (uint64_t)fdesc; return (uint64_t)fdesc;
if ((uint64_t) ++fdesc >= mod->arch.opd->sh_addr + mod->arch.opd->sh_size) if ((uint64_t) ++fdesc >= mod->arch.opd->sh_addr + mod->arch.opd->sh_size)
BUG(); BUG();
} }
/* Create new one */ /* Create new one */
fdesc->ip = value; fdesc->addr = value;
fdesc->gp = mod->arch.gp; fdesc->gp = mod->arch.gp;
return (uint64_t) fdesc; return (uint64_t) fdesc;
} }
......
...@@ -69,6 +69,7 @@ config PARISC ...@@ -69,6 +69,7 @@ config PARISC
select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_DYNAMIC_FTRACE_WITH_REGS
select HAVE_SOFTIRQ_ON_OWN_STACK if IRQSTACKS select HAVE_SOFTIRQ_ON_OWN_STACK if IRQSTACKS
select TRACE_IRQFLAGS_SUPPORT select TRACE_IRQFLAGS_SUPPORT
select HAVE_FUNCTION_DESCRIPTORS if 64BIT
help help
The PA-RISC microprocessor is designed by Hewlett-Packard and used The PA-RISC microprocessor is designed by Hewlett-Packard and used
......
...@@ -2,20 +2,14 @@ ...@@ -2,20 +2,14 @@
#ifndef _PARISC_SECTIONS_H #ifndef _PARISC_SECTIONS_H
#define _PARISC_SECTIONS_H #define _PARISC_SECTIONS_H
#ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
#include <asm/elf.h>
typedef Elf64_Fdesc func_desc_t;
#endif
/* nothing to see, move along */ /* nothing to see, move along */
#include <asm-generic/sections.h> #include <asm-generic/sections.h>
extern char __alt_instructions[], __alt_instructions_end[]; extern char __alt_instructions[], __alt_instructions_end[];
#ifdef CONFIG_64BIT
#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
#undef dereference_function_descriptor
void *dereference_function_descriptor(void *);
#undef dereference_kernel_function_descriptor
void *dereference_kernel_function_descriptor(void *);
#endif
#endif #endif
...@@ -263,27 +263,6 @@ __get_wchan(struct task_struct *p) ...@@ -263,27 +263,6 @@ __get_wchan(struct task_struct *p)
return 0; return 0;
} }
#ifdef CONFIG_64BIT
void *dereference_function_descriptor(void *ptr)
{
Elf64_Fdesc *desc = ptr;
void *p;
if (!get_kernel_nofault(p, (void *)&desc->addr))
ptr = p;
return ptr;
}
void *dereference_kernel_function_descriptor(void *ptr)
{
if (ptr < (void *)__start_opd ||
ptr >= (void *)__end_opd)
return ptr;
return dereference_function_descriptor(ptr);
}
#endif
static inline unsigned long brk_rnd(void) static inline unsigned long brk_rnd(void)
{ {
return (get_random_int() & BRK_RND_MASK) << PAGE_SHIFT; return (get_random_int() & BRK_RND_MASK) << PAGE_SHIFT;
......
...@@ -207,6 +207,7 @@ config PPC ...@@ -207,6 +207,7 @@ config PPC
select HAVE_EFFICIENT_UNALIGNED_ACCESS if !(CPU_LITTLE_ENDIAN && POWER7_CPU) select HAVE_EFFICIENT_UNALIGNED_ACCESS if !(CPU_LITTLE_ENDIAN && POWER7_CPU)
select HAVE_FAST_GUP select HAVE_FAST_GUP
select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_FUNCTION_DESCRIPTORS if PPC64 && !CPU_LITTLE_ENDIAN
select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_TRACER
......
...@@ -213,7 +213,7 @@ CHECKFLAGS += -m$(BITS) -D__powerpc__ -D__powerpc$(BITS)__ ...@@ -213,7 +213,7 @@ CHECKFLAGS += -m$(BITS) -D__powerpc__ -D__powerpc$(BITS)__
ifdef CONFIG_CPU_BIG_ENDIAN ifdef CONFIG_CPU_BIG_ENDIAN
CHECKFLAGS += -D__BIG_ENDIAN__ CHECKFLAGS += -D__BIG_ENDIAN__
else else
CHECKFLAGS += -D__LITTLE_ENDIAN__ CHECKFLAGS += -D__LITTLE_ENDIAN__ -D_CALL_ELF=2
endif endif
ifdef CONFIG_476FPE_ERR46 ifdef CONFIG_476FPE_ERR46
......
...@@ -118,7 +118,7 @@ static inline unsigned long ppc_function_entry(void *func) ...@@ -118,7 +118,7 @@ static inline unsigned long ppc_function_entry(void *func)
* function's descriptor. The first entry in the descriptor is the * function's descriptor. The first entry in the descriptor is the
* address of the function text. * address of the function text.
*/ */
return ((func_descr_t *)func)->entry; return ((struct func_desc *)func)->addr;
#else #else
return (unsigned long)func; return (unsigned long)func;
#endif #endif
......
...@@ -176,4 +176,10 @@ do { \ ...@@ -176,4 +176,10 @@ do { \
/* Relocate the kernel image to @final_address */ /* Relocate the kernel image to @final_address */
void relocate(unsigned long final_address); void relocate(unsigned long final_address);
struct func_desc {
unsigned long addr;
unsigned long toc;
unsigned long env;
};
#endif /* _ASM_POWERPC_ELF_H */ #endif /* _ASM_POWERPC_ELF_H */
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
typedef struct func_desc func_desc_t;
#endif
#include <asm-generic/sections.h> #include <asm-generic/sections.h>
extern char __head_end[]; extern char __head_end[];
...@@ -54,31 +58,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end) ...@@ -54,31 +58,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)
(unsigned long)_stext < end; (unsigned long)_stext < end;
} }
#ifdef PPC64_ELF_ABI_v1
#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
#undef dereference_function_descriptor
static inline void *dereference_function_descriptor(void *ptr)
{
struct ppc64_opd_entry *desc = ptr;
void *p;
if (!get_kernel_nofault(p, (void *)&desc->funcaddr))
ptr = p;
return ptr;
}
#undef dereference_kernel_function_descriptor
static inline void *dereference_kernel_function_descriptor(void *ptr)
{
if (ptr < (void *)__start_opd || ptr >= (void *)__end_opd)
return ptr;
return dereference_function_descriptor(ptr);
}
#endif /* PPC64_ELF_ABI_v1 */
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -23,12 +23,6 @@ ...@@ -23,12 +23,6 @@
typedef __vector128 vector128; typedef __vector128 vector128;
typedef struct {
unsigned long entry;
unsigned long toc;
unsigned long env;
} func_descr_t;
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* _ASM_POWERPC_TYPES_H */ #endif /* _ASM_POWERPC_TYPES_H */
...@@ -289,12 +289,4 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG]; ...@@ -289,12 +289,4 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];
/* Keep this the last entry. */ /* Keep this the last entry. */
#define R_PPC64_NUM 253 #define R_PPC64_NUM 253
/* There's actually a third entry here, but it's unused */
struct ppc64_opd_entry
{
unsigned long funcaddr;
unsigned long r2;
};
#endif /* _UAPI_ASM_POWERPC_ELF_H */ #endif /* _UAPI_ASM_POWERPC_ELF_H */
...@@ -33,20 +33,13 @@ ...@@ -33,20 +33,13 @@
#ifdef PPC64_ELF_ABI_v2 #ifdef PPC64_ELF_ABI_v2
/* An address is simply the address of the function. */
typedef unsigned long func_desc_t;
static func_desc_t func_desc(unsigned long addr) static func_desc_t func_desc(unsigned long addr)
{ {
return addr; func_desc_t desc = {
} .addr = addr,
static unsigned long func_addr(unsigned long addr) };
{
return addr; return desc;
}
static unsigned long stub_func_addr(func_desc_t func)
{
return func;
} }
/* PowerPC64 specific values for the Elf64_Sym st_other field. */ /* PowerPC64 specific values for the Elf64_Sym st_other field. */
...@@ -64,20 +57,9 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym) ...@@ -64,20 +57,9 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)
} }
#else #else
/* An address is address of the OPD entry, which contains address of fn. */
typedef struct ppc64_opd_entry func_desc_t;
static func_desc_t func_desc(unsigned long addr) static func_desc_t func_desc(unsigned long addr)
{ {
return *(struct ppc64_opd_entry *)addr; return *(struct func_desc *)addr;
}
static unsigned long func_addr(unsigned long addr)
{
return func_desc(addr).funcaddr;
}
static unsigned long stub_func_addr(func_desc_t func)
{
return func.funcaddr;
} }
static unsigned int local_entry_offset(const Elf64_Sym *sym) static unsigned int local_entry_offset(const Elf64_Sym *sym)
{ {
...@@ -94,6 +76,16 @@ void *dereference_module_function_descriptor(struct module *mod, void *ptr) ...@@ -94,6 +76,16 @@ void *dereference_module_function_descriptor(struct module *mod, void *ptr)
} }
#endif #endif
static unsigned long func_addr(unsigned long addr)
{
return func_desc(addr).addr;
}
static unsigned long stub_func_addr(func_desc_t func)
{
return func.addr;
}
#define STUB_MAGIC 0x73747562 /* stub */ #define STUB_MAGIC 0x73747562 /* stub */
/* Like PPC32, we need little trampolines to do > 24-bit jumps (into /* Like PPC32, we need little trampolines to do > 24-bit jumps (into
...@@ -188,7 +180,7 @@ static int relacmp(const void *_x, const void *_y) ...@@ -188,7 +180,7 @@ static int relacmp(const void *_x, const void *_y)
static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
const Elf64_Shdr *sechdrs) const Elf64_Shdr *sechdrs)
{ {
/* One extra reloc so it's always 0-funcaddr terminated */ /* One extra reloc so it's always 0-addr terminated */
unsigned long relocs = 1; unsigned long relocs = 1;
unsigned i; unsigned i;
......
...@@ -445,4 +445,10 @@ void __init pt_regs_check(void) ...@@ -445,4 +445,10 @@ void __init pt_regs_check(void)
* real registers. * real registers.
*/ */
BUILD_BUG_ON(PT_DSCR < sizeof(struct user_pt_regs) / sizeof(unsigned long)); BUILD_BUG_ON(PT_DSCR < sizeof(struct user_pt_regs) / sizeof(unsigned long));
#ifdef PPC64_ELF_ABI_v1
BUILD_BUG_ON(!IS_ENABLED(CONFIG_HAVE_FUNCTION_DESCRIPTORS));
#else
BUILD_BUG_ON(IS_ENABLED(CONFIG_HAVE_FUNCTION_DESCRIPTORS));
#endif
} }
...@@ -936,11 +936,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, ...@@ -936,11 +936,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
* descriptor is the entry address of signal and the second * descriptor is the entry address of signal and the second
* entry is the TOC value we need to use. * entry is the TOC value we need to use.
*/ */
func_descr_t __user *funct_desc_ptr = struct func_desc __user *ptr =
(func_descr_t __user *) ksig->ka.sa.sa_handler; (struct func_desc __user *)ksig->ka.sa.sa_handler;
err |= get_user(regs->ctr, &funct_desc_ptr->entry); err |= get_user(regs->ctr, &ptr->addr);
err |= get_user(regs->gpr[2], &funct_desc_ptr->toc); err |= get_user(regs->gpr[2], &ptr->toc);
} }
/* enter the signal handler in native-endian mode */ /* enter the signal handler in native-endian mode */
......
...@@ -149,6 +149,7 @@ static const struct crashtype crashtypes[] = { ...@@ -149,6 +149,7 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(WRITE_RO), CRASHTYPE(WRITE_RO),
CRASHTYPE(WRITE_RO_AFTER_INIT), CRASHTYPE(WRITE_RO_AFTER_INIT),
CRASHTYPE(WRITE_KERN), CRASHTYPE(WRITE_KERN),
CRASHTYPE(WRITE_OPD),
CRASHTYPE(REFCOUNT_INC_OVERFLOW), CRASHTYPE(REFCOUNT_INC_OVERFLOW),
CRASHTYPE(REFCOUNT_ADD_OVERFLOW), CRASHTYPE(REFCOUNT_ADD_OVERFLOW),
CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW), CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW),
......
...@@ -106,6 +106,7 @@ void __init lkdtm_perms_init(void); ...@@ -106,6 +106,7 @@ void __init lkdtm_perms_init(void);
void lkdtm_WRITE_RO(void); void lkdtm_WRITE_RO(void);
void lkdtm_WRITE_RO_AFTER_INIT(void); void lkdtm_WRITE_RO_AFTER_INIT(void);
void lkdtm_WRITE_KERN(void); void lkdtm_WRITE_KERN(void);
void lkdtm_WRITE_OPD(void);
void lkdtm_EXEC_DATA(void); void lkdtm_EXEC_DATA(void);
void lkdtm_EXEC_STACK(void); void lkdtm_EXEC_STACK(void);
void lkdtm_EXEC_KMALLOC(void); void lkdtm_EXEC_KMALLOC(void);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/sections.h>
/* Whether or not to fill the target memory area with do_nothing(). */ /* Whether or not to fill the target memory area with do_nothing(). */
#define CODE_WRITE true #define CODE_WRITE true
...@@ -21,7 +22,7 @@ ...@@ -21,7 +22,7 @@
/* This is non-const, so it will end up in the .data section. */ /* This is non-const, so it will end up in the .data section. */
static u8 data_area[EXEC_SIZE]; static u8 data_area[EXEC_SIZE];
/* This is cost, so it will end up in the .rodata section. */ /* This is const, so it will end up in the .rodata section. */
static const unsigned long rodata = 0xAA55AA55; static const unsigned long rodata = 0xAA55AA55;
/* This is marked __ro_after_init, so it should ultimately be .rodata. */ /* This is marked __ro_after_init, so it should ultimately be .rodata. */
...@@ -31,31 +32,51 @@ static unsigned long ro_after_init __ro_after_init = 0x55AA5500; ...@@ -31,31 +32,51 @@ static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
* This just returns to the caller. It is designed to be copied into * This just returns to the caller. It is designed to be copied into
* non-executable memory regions. * non-executable memory regions.
*/ */
static void do_nothing(void) static noinline void do_nothing(void)
{ {
return; return;
} }
/* Must immediately follow do_nothing for size calculuations to work out. */ /* Must immediately follow do_nothing for size calculuations to work out. */
static void do_overwritten(void) static noinline void do_overwritten(void)
{ {
pr_info("do_overwritten wasn't overwritten!\n"); pr_info("do_overwritten wasn't overwritten!\n");
return; return;
} }
static noinline void do_almost_nothing(void)
{
pr_info("do_nothing was hijacked!\n");
}
static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
{
if (!have_function_descriptors())
return dst;
memcpy(fdesc, do_nothing, sizeof(*fdesc));
fdesc->addr = (unsigned long)dst;
barrier();
return fdesc;
}
static noinline void execute_location(void *dst, bool write) static noinline void execute_location(void *dst, bool write)
{ {
void (*func)(void) = dst; void (*func)(void);
func_desc_t fdesc;
void *do_nothing_text = dereference_function_descriptor(do_nothing);
pr_info("attempting ok execution at %px\n", do_nothing); pr_info("attempting ok execution at %px\n", do_nothing_text);
do_nothing(); do_nothing();
if (write == CODE_WRITE) { if (write == CODE_WRITE) {
memcpy(dst, do_nothing, EXEC_SIZE); memcpy(dst, do_nothing_text, EXEC_SIZE);
flush_icache_range((unsigned long)dst, flush_icache_range((unsigned long)dst,
(unsigned long)dst + EXEC_SIZE); (unsigned long)dst + EXEC_SIZE);
} }
pr_info("attempting bad execution at %px\n", func); pr_info("attempting bad execution at %px\n", dst);
func = setup_function_descriptor(&fdesc, dst);
func(); func();
pr_err("FAIL: func returned\n"); pr_err("FAIL: func returned\n");
} }
...@@ -65,16 +86,19 @@ static void execute_user_location(void *dst) ...@@ -65,16 +86,19 @@ static void execute_user_location(void *dst)
int copied; int copied;
/* Intentionally crossing kernel/user memory boundary. */ /* Intentionally crossing kernel/user memory boundary. */
void (*func)(void) = dst; void (*func)(void);
func_desc_t fdesc;
void *do_nothing_text = dereference_function_descriptor(do_nothing);
pr_info("attempting ok execution at %px\n", do_nothing); pr_info("attempting ok execution at %px\n", do_nothing_text);
do_nothing(); do_nothing();
copied = access_process_vm(current, (unsigned long)dst, do_nothing, copied = access_process_vm(current, (unsigned long)dst, do_nothing_text,
EXEC_SIZE, FOLL_WRITE); EXEC_SIZE, FOLL_WRITE);
if (copied < EXEC_SIZE) if (copied < EXEC_SIZE)
return; return;
pr_info("attempting bad execution at %px\n", func); pr_info("attempting bad execution at %px\n", dst);
func = setup_function_descriptor(&fdesc, dst);
func(); func();
pr_err("FAIL: func returned\n"); pr_err("FAIL: func returned\n");
} }
...@@ -113,8 +137,9 @@ void lkdtm_WRITE_KERN(void) ...@@ -113,8 +137,9 @@ void lkdtm_WRITE_KERN(void)
size_t size; size_t size;
volatile unsigned char *ptr; volatile unsigned char *ptr;
size = (unsigned long)do_overwritten - (unsigned long)do_nothing; size = (unsigned long)dereference_function_descriptor(do_overwritten) -
ptr = (unsigned char *)do_overwritten; (unsigned long)dereference_function_descriptor(do_nothing);
ptr = dereference_function_descriptor(do_overwritten);
pr_info("attempting bad %zu byte write at %px\n", size, ptr); pr_info("attempting bad %zu byte write at %px\n", size, ptr);
memcpy((void *)ptr, (unsigned char *)do_nothing, size); memcpy((void *)ptr, (unsigned char *)do_nothing, size);
...@@ -124,6 +149,23 @@ void lkdtm_WRITE_KERN(void) ...@@ -124,6 +149,23 @@ void lkdtm_WRITE_KERN(void)
do_overwritten(); do_overwritten();
} }
void lkdtm_WRITE_OPD(void)
{
size_t size = sizeof(func_desc_t);
void (*func)(void) = do_nothing;
if (!have_function_descriptors()) {
pr_info("XFAIL: Platform doesn't use function descriptors.\n");
return;
}
pr_info("attempting bad %zu bytes write at %px\n", size, do_nothing);
memcpy(do_nothing, do_almost_nothing, size);
pr_err("FAIL: survived bad write\n");
asm("" : "=m"(func));
func();
}
void lkdtm_EXEC_DATA(void) void lkdtm_EXEC_DATA(void)
{ {
execute_location(data_area, CODE_WRITE); execute_location(data_area, CODE_WRITE);
...@@ -151,7 +193,8 @@ void lkdtm_EXEC_VMALLOC(void) ...@@ -151,7 +193,8 @@ void lkdtm_EXEC_VMALLOC(void)
void lkdtm_EXEC_RODATA(void) void lkdtm_EXEC_RODATA(void)
{ {
execute_location(lkdtm_rodata_do_nothing, CODE_AS_IS); execute_location(dereference_function_descriptor(lkdtm_rodata_do_nothing),
CODE_AS_IS);
} }
void lkdtm_EXEC_USERSPACE(void) void lkdtm_EXEC_USERSPACE(void)
......
...@@ -59,11 +59,24 @@ extern char __noinstr_text_start[], __noinstr_text_end[]; ...@@ -59,11 +59,24 @@ extern char __noinstr_text_start[], __noinstr_text_end[];
extern __visible const void __nosave_begin, __nosave_end; extern __visible const void __nosave_begin, __nosave_end;
/* Function descriptor handling (if any). Override in asm/sections.h */ /* Function descriptor handling (if any). Override in asm/sections.h */
#ifndef dereference_function_descriptor #ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
void *dereference_function_descriptor(void *ptr);
void *dereference_kernel_function_descriptor(void *ptr);
#else
#define dereference_function_descriptor(p) ((void *)(p)) #define dereference_function_descriptor(p) ((void *)(p))
#define dereference_kernel_function_descriptor(p) ((void *)(p)) #define dereference_kernel_function_descriptor(p) ((void *)(p))
/* An address is simply the address of the function. */
typedef struct {
unsigned long addr;
} func_desc_t;
#endif #endif
static inline bool have_function_descriptors(void)
{
return IS_ENABLED(CONFIG_HAVE_FUNCTION_DESCRIPTORS);
}
/** /**
* memory_contains - checks if an object is contained within a memory region * memory_contains - checks if an object is contained within a memory region
* @begin: virtual address of the beginning of the memory region * @begin: virtual address of the beginning of the memory region
......
...@@ -48,7 +48,7 @@ static inline int is_ksym_addr(unsigned long addr) ...@@ -48,7 +48,7 @@ static inline int is_ksym_addr(unsigned long addr)
static inline void *dereference_symbol_descriptor(void *ptr) static inline void *dereference_symbol_descriptor(void *ptr)
{ {
#ifdef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR #ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
struct module *mod; struct module *mod;
ptr = dereference_kernel_function_descriptor(ptr); ptr = dereference_kernel_function_descriptor(ptr);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
*/ */
#include <linux/elf.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/extable.h> #include <linux/extable.h>
...@@ -132,12 +133,33 @@ int kernel_text_address(unsigned long addr) ...@@ -132,12 +133,33 @@ int kernel_text_address(unsigned long addr)
} }
/* /*
* On some architectures (PPC64, IA64) function pointers * On some architectures (PPC64, IA64, PARISC) function pointers
* are actually only tokens to some data that then holds the * are actually only tokens to some data that then holds the
* real function address. As a result, to find if a function * real function address. As a result, to find if a function
* pointer is part of the kernel text, we need to do some * pointer is part of the kernel text, we need to do some
* special dereferencing first. * special dereferencing first.
*/ */
#ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
void *dereference_function_descriptor(void *ptr)
{
func_desc_t *desc = ptr;
void *p;
if (!get_kernel_nofault(p, (void *)&desc->addr))
ptr = p;
return ptr;
}
EXPORT_SYMBOL_GPL(dereference_function_descriptor);
void *dereference_kernel_function_descriptor(void *ptr)
{
if (ptr < (void *)__start_opd || ptr >= (void *)__end_opd)
return ptr;
return dereference_function_descriptor(ptr);
}
#endif
int func_ptr_is_kernel_text(void *ptr) int func_ptr_is_kernel_text(void *ptr)
{ {
unsigned long addr; unsigned long addr;
......
...@@ -44,6 +44,7 @@ ACCESS_NULL ...@@ -44,6 +44,7 @@ ACCESS_NULL
WRITE_RO WRITE_RO
WRITE_RO_AFTER_INIT WRITE_RO_AFTER_INIT
WRITE_KERN WRITE_KERN
WRITE_OPD
REFCOUNT_INC_OVERFLOW REFCOUNT_INC_OVERFLOW
REFCOUNT_ADD_OVERFLOW REFCOUNT_ADD_OVERFLOW
REFCOUNT_INC_NOT_ZERO_OVERFLOW REFCOUNT_INC_NOT_ZERO_OVERFLOW
......
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