2021-10-14 06:26:21

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 00/13] 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.

Patch 8 is not absolutely necessary but it is a good trivial cleanup.

Changes in v2:
- Addressed received comments
- Moved dereference_[kernel]_function_descriptor() out of line
- Added patches to remove func_descr_t and func_desc_t in powerpc
- Using func_desc_t instead of funct_descr_t
- Renamed HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR to HAVE_FUNCTION_DESCRIPTORS
- Added a new lkdtm test to check protection of function descriptors

Christophe Leroy (13):
powerpc: Move 'struct ppc64_opd_entry' back into asm/elf.h
powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'
powerpc: Remove func_descr_t
powerpc: Prepare func_desc_t for refactorisation
ia64: Rename 'ip' to 'addr' in 'struct fdesc'
asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs
asm-generic: Define 'func_desc_t' to commonly describe function
descriptors
asm-generic: Refactor dereference_[kernel]_function_descriptor()
lkdtm: Force do_nothing() out of line
lkdtm: Really write into kernel text in WRITE_KERN
lkdtm: Fix lkdtm_EXEC_RODATA()
lkdtm: Fix execute_[user]_location()
lkdtm: Add a test for function descriptors protection

arch/ia64/include/asm/elf.h | 2 +-
arch/ia64/include/asm/sections.h | 25 ++-------
arch/ia64/kernel/module.c | 6 +--
arch/parisc/include/asm/sections.h | 17 +++---
arch/parisc/kernel/process.c | 21 --------
arch/powerpc/include/asm/code-patching.h | 2 +-
arch/powerpc/include/asm/elf.h | 6 +++
arch/powerpc/include/asm/sections.h | 30 ++---------
arch/powerpc/include/asm/types.h | 6 ---
arch/powerpc/include/uapi/asm/elf.h | 8 ---
arch/powerpc/kernel/module_64.c | 38 +++++--------
arch/powerpc/kernel/signal_64.c | 8 +--
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/lkdtm.h | 1 +
drivers/misc/lkdtm/perms.c | 68 ++++++++++++++++++++----
include/asm-generic/sections.h | 13 ++++-
include/linux/kallsyms.h | 2 +-
kernel/extable.c | 23 +++++++-
18 files changed, 138 insertions(+), 139 deletions(-)

--
2.31.1


2021-10-14 06:26:34

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 02/13] powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'

There are three architectures with function descriptors, try to
have common names for the address they contain in order to
refactor some functions into generic functions later.

powerpc has 'funcaddr'
ia64 has 'ip'
parisc has 'addr'

Vote for 'addr' and update 'struct ppc64_opd_entry' accordingly.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
arch/powerpc/include/asm/elf.h | 2 +-
arch/powerpc/include/asm/sections.h | 2 +-
arch/powerpc/kernel/module_64.c | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
index a4406714c060..bb0f278f9ed4 100644
--- a/arch/powerpc/include/asm/elf.h
+++ b/arch/powerpc/include/asm/elf.h
@@ -178,7 +178,7 @@ void relocate(unsigned long final_address);

/* There's actually a third entry here, but it's unused */
struct ppc64_opd_entry {
- unsigned long funcaddr;
+ unsigned long addr;
unsigned long r2;
};

diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
index 6e4af4492a14..32e7035863ac 100644
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -77,7 +77,7 @@ static inline void *dereference_function_descriptor(void *ptr)
struct ppc64_opd_entry *desc = ptr;
void *p;

- if (!get_kernel_nofault(p, (void *)&desc->funcaddr))
+ if (!get_kernel_nofault(p, (void *)&desc->addr))
ptr = p;
return ptr;
}
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index 6baa676e7cb6..82908c9be627 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -72,11 +72,11 @@ static func_desc_t func_desc(unsigned long addr)
}
static unsigned long func_addr(unsigned long addr)
{
- return func_desc(addr).funcaddr;
+ return func_desc(addr).addr;
}
static unsigned long stub_func_addr(func_desc_t func)
{
- return func.funcaddr;
+ return func.addr;
}
static unsigned int local_entry_offset(const Elf64_Sym *sym)
{
@@ -187,7 +187,7 @@ static int relacmp(const void *_x, const void *_y)
static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
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 i;

--
2.31.1

2021-10-14 06:26:38

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 13/13] lkdtm: Add a test for function descriptors protection

Add WRITE_OPD to check that you can't modify function
descriptors.

Gives the following result when function descriptors are
not protected:

lkdtm: Performing direct entry WRITE_OPD
lkdtm: attempting bad 16 bytes write at c00000000269b358
lkdtm: FAIL: survived bad write
lkdtm: do_nothing was hijacked!

Looks like a standard compiler barrier(); is not enough to force
GCC to use the modified function descriptor. Add to add a fake empty
inline assembly to force GCC to reload the function descriptor.

Signed-off-by: Christophe Leroy <[email protected]>
---
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/lkdtm.h | 1 +
drivers/misc/lkdtm/perms.c | 22 ++++++++++++++++++++++
3 files changed, 24 insertions(+)

diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index fe6fd34b8caf..de092aa03b5d 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -148,6 +148,7 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(WRITE_RO),
CRASHTYPE(WRITE_RO_AFTER_INIT),
CRASHTYPE(WRITE_KERN),
+ CRASHTYPE(WRITE_OPD),
CRASHTYPE(REFCOUNT_INC_OVERFLOW),
CRASHTYPE(REFCOUNT_ADD_OVERFLOW),
CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW),
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index c212a253edde..188bd0fd6575 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -105,6 +105,7 @@ void __init lkdtm_perms_init(void);
void lkdtm_WRITE_RO(void);
void lkdtm_WRITE_RO_AFTER_INIT(void);
void lkdtm_WRITE_KERN(void);
+void lkdtm_WRITE_OPD(void);
void lkdtm_EXEC_DATA(void);
void lkdtm_EXEC_STACK(void);
void lkdtm_EXEC_KMALLOC(void);
diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
index 96b3ebfcb8ed..3870bc82d40d 100644
--- a/drivers/misc/lkdtm/perms.c
+++ b/drivers/misc/lkdtm/perms.c
@@ -44,6 +44,11 @@ static noinline void do_overwritten(void)
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)
{
memcpy(fdesc, do_nothing, sizeof(*fdesc));
@@ -143,6 +148,23 @@ void lkdtm_WRITE_KERN(void)
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("Platform doesn't have 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)
{
execute_location(data_area, CODE_WRITE);
--
2.31.1

2021-10-14 06:26:43

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 07/13] asm-generic: Define 'func_desc_t' to commonly describe function descriptors

We have three architectures using function descriptors, each with its
own name.

Add a common typedef that can be used in generic code.

Also add a stub typedef for architecture without function descriptors,
to avoid a forest of #ifdefs.

It replaces the similar func_desc_t previously defined in
arch/powerpc/kernel/module_64.c

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
arch/ia64/include/asm/sections.h | 1 +
arch/parisc/include/asm/sections.h | 2 ++
arch/powerpc/include/asm/sections.h | 1 +
arch/powerpc/kernel/module_64.c | 8 --------
include/asm-generic/sections.h | 3 +++
5 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
index 6e55e545bf02..1aaed8882294 100644
--- a/arch/ia64/include/asm/sections.h
+++ b/arch/ia64/include/asm/sections.h
@@ -11,6 +11,7 @@
#include <linux/uaccess.h>

#define HAVE_FUNCTION_DESCRIPTORS 1
+typedef struct fdesc func_desc_t;

#include <asm-generic/sections.h>

diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
index 85149a89ff3e..37b34b357cb5 100644
--- a/arch/parisc/include/asm/sections.h
+++ b/arch/parisc/include/asm/sections.h
@@ -3,7 +3,9 @@
#define _PARISC_SECTIONS_H

#ifdef CONFIG_64BIT
+#include <asm/elf.h>
#define HAVE_FUNCTION_DESCRIPTORS 1
+typedef Elf64_Fdesc func_desc_t;
#endif

/* nothing to see, move along */
diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
index bba97b8c38cf..1322d7b2f1a3 100644
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -10,6 +10,7 @@

#ifdef PPC64_ELF_ABI_v1
#define HAVE_FUNCTION_DESCRIPTORS 1
+typedef struct ppc64_opd_entry func_desc_t;
#endif

#include <asm-generic/sections.h>
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index dc99a3f6cee2..affda7698242 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -32,11 +32,6 @@

#ifdef PPC64_ELF_ABI_v2

-/* An address is simply the address of the function. */
-typedef struct {
- unsigned long addr;
-} func_desc_t;
-
static func_desc_t func_desc(unsigned long addr)
{
return (func_desc_t){addr};
@@ -57,9 +52,6 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)
}
#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)
{
return *(func_desc_t *)addr;
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index b677e926e6b3..cbec7d5f1678 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -63,6 +63,9 @@ extern __visible const void __nosave_begin, __nosave_end;
#else
#define dereference_function_descriptor(p) ((void *)(p))
#define dereference_kernel_function_descriptor(p) ((void *)(p))
+typedef struct {
+ unsigned long addr;
+} func_desc_t;
#endif

/* random extra sections (if any). Override
--
2.31.1

2021-10-14 06:27:33

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 03/13] powerpc: Remove func_descr_t

'func_descr_t' is redundant with 'struct ppc64_opd_entry'

Remove it.

Signed-off-by: Christophe Leroy <[email protected]>
---
arch/powerpc/include/asm/code-patching.h | 2 +-
arch/powerpc/include/asm/types.h | 6 ------
arch/powerpc/kernel/signal_64.c | 8 ++++----
3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
index 4ba834599c4d..f3445188d319 100644
--- a/arch/powerpc/include/asm/code-patching.h
+++ b/arch/powerpc/include/asm/code-patching.h
@@ -110,7 +110,7 @@ static inline unsigned long ppc_function_entry(void *func)
* function's descriptor. The first entry in the descriptor is the
* address of the function text.
*/
- return ((func_descr_t *)func)->entry;
+ return ((struct ppc64_opd_entry *)func)->addr;
#else
return (unsigned long)func;
#endif
diff --git a/arch/powerpc/include/asm/types.h b/arch/powerpc/include/asm/types.h
index f1630c553efe..97da77bc48c9 100644
--- a/arch/powerpc/include/asm/types.h
+++ b/arch/powerpc/include/asm/types.h
@@ -23,12 +23,6 @@

typedef __vector128 vector128;

-typedef struct {
- unsigned long entry;
- unsigned long toc;
- unsigned long env;
-} func_descr_t;
-
#endif /* __ASSEMBLY__ */

#endif /* _ASM_POWERPC_TYPES_H */
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 1831bba0582e..63ddbe7b108c 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -933,11 +933,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
* descriptor is the entry address of signal and the second
* entry is the TOC value we need to use.
*/
- func_descr_t __user *funct_desc_ptr =
- (func_descr_t __user *) ksig->ka.sa.sa_handler;
+ struct ppc64_opd_entry __user *funct_desc_ptr =
+ (struct ppc64_opd_entry __user *)ksig->ka.sa.sa_handler;

- err |= get_user(regs->ctr, &funct_desc_ptr->entry);
- err |= get_user(regs->gpr[2], &funct_desc_ptr->toc);
+ err |= get_user(regs->ctr, &funct_desc_ptr->addr);
+ err |= get_user(regs->gpr[2], &funct_desc_ptr->r2);
}

/* enter the signal handler in native-endian mode */
--
2.31.1

2021-10-14 06:28:04

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 08/13] asm-generic: Refactor dereference_[kernel]_function_descriptor()

dereference_function_descriptor() and
dereference_kernel_function_descriptor() are identical on the
three architectures implementing them.

Make them common and put them out-of-line in kernel/extable.c
which is one of the users and has similar type of functions.

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
arch/ia64/include/asm/sections.h | 19 -------------------
arch/parisc/include/asm/sections.h | 9 ---------
arch/parisc/kernel/process.c | 21 ---------------------
arch/powerpc/include/asm/sections.h | 23 -----------------------
include/asm-generic/sections.h | 2 ++
kernel/extable.c | 23 ++++++++++++++++++++++-
6 files changed, 24 insertions(+), 73 deletions(-)

diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
index 1aaed8882294..96c9bb500c34 100644
--- a/arch/ia64/include/asm/sections.h
+++ b/arch/ia64/include/asm/sections.h
@@ -31,23 +31,4 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
extern char __start_unwind[], __end_unwind[];
extern char __start_ivt_text[], __end_ivt_text[];

-#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->addr))
- 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 */
diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
index 37b34b357cb5..6b1fe22baaf5 100644
--- a/arch/parisc/include/asm/sections.h
+++ b/arch/parisc/include/asm/sections.h
@@ -13,13 +13,4 @@ typedef Elf64_Fdesc func_desc_t;

extern char __alt_instructions[], __alt_instructions_end[];

-#ifdef CONFIG_64BIT
-
-#undef dereference_function_descriptor
-void *dereference_function_descriptor(void *);
-
-#undef dereference_kernel_function_descriptor
-void *dereference_kernel_function_descriptor(void *);
-#endif
-
#endif
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 38ec4ae81239..7382576b52a8 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -266,27 +266,6 @@ get_wchan(struct task_struct *p)
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)
{
return (get_random_int() & BRK_RND_MASK) << PAGE_SHIFT;
diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
index 1322d7b2f1a3..fbfe1957edbe 100644
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -72,29 +72,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)
(unsigned long)_stext < end;
}

-#ifdef PPC64_ELF_ABI_v1
-
-#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->addr))
- 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 /* __KERNEL__ */
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index cbec7d5f1678..76163883c6ff 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -60,6 +60,8 @@ extern __visible const void __nosave_begin, __nosave_end;

/* Function descriptor handling (if any). Override in asm/sections.h */
#ifdef 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_kernel_function_descriptor(p) ((void *)(p))
diff --git a/kernel/extable.c b/kernel/extable.c
index b0ea5eb0c3b4..013ccffade11 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -3,6 +3,7 @@
Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.

*/
+#include <linux/elf.h>
#include <linux/ftrace.h>
#include <linux/memory.h>
#include <linux/extable.h>
@@ -159,12 +160,32 @@ 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
* real function address. As a result, to find if a function
* pointer is part of the kernel text, we need to do some
* special dereferencing first.
*/
+#ifdef 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;
+}
+
+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)
{
unsigned long addr;
--
2.31.1

2021-10-14 06:28:04

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 12/13] lkdtm: Fix execute_[user]_location()

execute_location() and execute_user_location() intent
to copy do_nothing() text and execute it at a new location.
However, at the time being it doesn't copy do_nothing() function
but do_nothing() function descriptor which still points to the
original text. So at the end it still executes do_nothing() at
its original location allthough using a copied function descriptor.

So, fix that by really copying do_nothing() text and build a new
function descriptor by copying do_nothing() function descriptor and
updating the target address with the new location.

Also fix the displayed addresses by dereferencing do_nothing()
function descriptor.

Signed-off-by: Christophe Leroy <[email protected]>
---
drivers/misc/lkdtm/perms.c | 25 +++++++++++++++++++++----
include/asm-generic/sections.h | 5 +++++
2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
index 5266dc28df6e..96b3ebfcb8ed 100644
--- a/drivers/misc/lkdtm/perms.c
+++ b/drivers/misc/lkdtm/perms.c
@@ -44,19 +44,32 @@ static noinline void do_overwritten(void)
return;
}

+static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
+{
+ memcpy(fdesc, do_nothing, sizeof(*fdesc));
+ fdesc->addr = (unsigned long)dst;
+ barrier();
+
+ return fdesc;
+}
+
static noinline void execute_location(void *dst, bool write)
{
void (*func)(void) = dst;
+ 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();

if (write == CODE_WRITE) {
- memcpy(dst, do_nothing, EXEC_SIZE);
+ memcpy(dst, do_nothing_text, EXEC_SIZE);
flush_icache_range((unsigned long)dst,
(unsigned long)dst + EXEC_SIZE);
}
pr_info("attempting bad execution at %px\n", func);
+ if (have_function_descriptors())
+ func = setup_function_descriptor(&fdesc, dst);
func();
pr_err("FAIL: func returned\n");
}
@@ -67,15 +80,19 @@ static void execute_user_location(void *dst)

/* Intentionally crossing kernel/user memory boundary. */
void (*func)(void) = dst;
+ 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();

- 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);
if (copied < EXEC_SIZE)
return;
pr_info("attempting bad execution at %px\n", func);
+ if (have_function_descriptors())
+ func = setup_function_descriptor(&fdesc, dst);
func();
pr_err("FAIL: func returned\n");
}
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index 76163883c6ff..d225318538bd 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -70,6 +70,11 @@ typedef struct {
} func_desc_t;
#endif

+static inline bool have_function_descriptors(void)
+{
+ return __is_defined(HAVE_FUNCTION_DESCRIPTORS);
+}
+
/* random extra sections (if any). Override
* in asm/sections.h */
#ifndef arch_is_kernel_text
--
2.31.1

2021-10-14 06:28:11

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v2 06/13] asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs

Replace HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR by
HAVE_FUNCTION_DESCRIPTORS and use it instead of
'dereference_function_descriptor' macro to know
whether an arch has function descriptors.

To limit churn in one of the following patches, use
an #ifdef/#else construct with empty first part
instead of an #ifndef in asm-generic/sections.h

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
arch/ia64/include/asm/sections.h | 5 +++--
arch/parisc/include/asm/sections.h | 6 ++++--
arch/powerpc/include/asm/sections.h | 6 ++++--
include/asm-generic/sections.h | 3 ++-
include/linux/kallsyms.h | 2 +-
5 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
index 35f24e52149a..6e55e545bf02 100644
--- a/arch/ia64/include/asm/sections.h
+++ b/arch/ia64/include/asm/sections.h
@@ -9,6 +9,9 @@

#include <linux/elf.h>
#include <linux/uaccess.h>
+
+#define HAVE_FUNCTION_DESCRIPTORS 1
+
#include <asm-generic/sections.h>

extern char __phys_per_cpu_start[];
@@ -27,8 +30,6 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
extern char __start_unwind[], __end_unwind[];
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)
{
diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
index bb52aea0cb21..85149a89ff3e 100644
--- a/arch/parisc/include/asm/sections.h
+++ b/arch/parisc/include/asm/sections.h
@@ -2,6 +2,10 @@
#ifndef _PARISC_SECTIONS_H
#define _PARISC_SECTIONS_H

+#ifdef CONFIG_64BIT
+#define HAVE_FUNCTION_DESCRIPTORS 1
+#endif
+
/* nothing to see, move along */
#include <asm-generic/sections.h>

@@ -9,8 +13,6 @@ 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 *);

diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
index 32e7035863ac..bba97b8c38cf 100644
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -8,6 +8,10 @@

#define arch_is_kernel_initmem_freed arch_is_kernel_initmem_freed

+#ifdef PPC64_ELF_ABI_v1
+#define HAVE_FUNCTION_DESCRIPTORS 1
+#endif
+
#include <asm-generic/sections.h>

extern bool init_mem_is_free;
@@ -69,8 +73,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)

#ifdef PPC64_ELF_ABI_v1

-#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
-
#undef dereference_function_descriptor
static inline void *dereference_function_descriptor(void *ptr)
{
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index d16302d3eb59..b677e926e6b3 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -59,7 +59,8 @@ extern char __noinstr_text_start[], __noinstr_text_end[];
extern __visible const void __nosave_begin, __nosave_end;

/* Function descriptor handling (if any). Override in asm/sections.h */
-#ifndef dereference_function_descriptor
+#ifdef HAVE_FUNCTION_DESCRIPTORS
+#else
#define dereference_function_descriptor(p) ((void *)(p))
#define dereference_kernel_function_descriptor(p) ((void *)(p))
#endif
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index a1d6fc82d7f0..9f277baeb559 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -57,7 +57,7 @@ static inline int is_ksym_addr(unsigned long addr)

static inline void *dereference_symbol_descriptor(void *ptr)
{
-#ifdef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR
+#ifdef HAVE_FUNCTION_DESCRIPTORS
struct module *mod;

ptr = dereference_kernel_function_descriptor(ptr);
--
2.31.1

2021-10-14 06:54:32

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH v2 07/13] asm-generic: Define 'func_desc_t' to commonly describe function descriptors

On Thu, Oct 14, 2021 at 7:49 AM Christophe Leroy
<[email protected]> wrote:
>
> We have three architectures using function descriptors, each with its
> own name.
>
> Add a common typedef that can be used in generic code.
>
> Also add a stub typedef for architecture without function descriptors,
> to avoid a forest of #ifdefs.
>
> It replaces the similar func_desc_t previously defined in
> arch/powerpc/kernel/module_64.c
>
> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Christophe Leroy <[email protected]>

Acked-by: Arnd Bergmann <[email protected]>

2021-10-15 02:58:42

by Daniel Axtens

[permalink] [raw]
Subject: Re: [PATCH v2 00/13] Fix LKDTM for PPC64/IA64/PARISC

Christophe Leroy <[email protected]> writes:

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

Just to nitpick, it's powerpc 64-bit using the ELFv1 ABI. [1]

The ELFv2 ABI [2] doesn't use function descriptors. (ELFv2 is used
primarily for ppc64le, but some people like musl support it for BE as
well.)

This doesn't affect the correctness or desirability of your changes, it
was just bugging me when I was reading the commit messages :-)

Kind regards,
Daniel

[1] See e.g. https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
[2] https://openpowerfoundation.org/wp-content/uploads/2016/03/ABI64BitOpenPOWERv1.1_16July2015_pub4.pdf


> 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.
>
> Patch 8 is not absolutely necessary but it is a good trivial cleanup.
>
> Changes in v2:
> - Addressed received comments
> - Moved dereference_[kernel]_function_descriptor() out of line
> - Added patches to remove func_descr_t and func_desc_t in powerpc
> - Using func_desc_t instead of funct_descr_t
> - Renamed HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR to HAVE_FUNCTION_DESCRIPTORS
> - Added a new lkdtm test to check protection of function descriptors
>
> Christophe Leroy (13):
> powerpc: Move 'struct ppc64_opd_entry' back into asm/elf.h
> powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'
> powerpc: Remove func_descr_t
> powerpc: Prepare func_desc_t for refactorisation
> ia64: Rename 'ip' to 'addr' in 'struct fdesc'
> asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs
> asm-generic: Define 'func_desc_t' to commonly describe function
> descriptors
> asm-generic: Refactor dereference_[kernel]_function_descriptor()
> lkdtm: Force do_nothing() out of line
> lkdtm: Really write into kernel text in WRITE_KERN
> lkdtm: Fix lkdtm_EXEC_RODATA()
> lkdtm: Fix execute_[user]_location()
> lkdtm: Add a test for function descriptors protection
>
> arch/ia64/include/asm/elf.h | 2 +-
> arch/ia64/include/asm/sections.h | 25 ++-------
> arch/ia64/kernel/module.c | 6 +--
> arch/parisc/include/asm/sections.h | 17 +++---
> arch/parisc/kernel/process.c | 21 --------
> arch/powerpc/include/asm/code-patching.h | 2 +-
> arch/powerpc/include/asm/elf.h | 6 +++
> arch/powerpc/include/asm/sections.h | 30 ++---------
> arch/powerpc/include/asm/types.h | 6 ---
> arch/powerpc/include/uapi/asm/elf.h | 8 ---
> arch/powerpc/kernel/module_64.c | 38 +++++--------
> arch/powerpc/kernel/signal_64.c | 8 +--
> drivers/misc/lkdtm/core.c | 1 +
> drivers/misc/lkdtm/lkdtm.h | 1 +
> drivers/misc/lkdtm/perms.c | 68 ++++++++++++++++++++----
> include/asm-generic/sections.h | 13 ++++-
> include/linux/kallsyms.h | 2 +-
> kernel/extable.c | 23 +++++++-
> 18 files changed, 138 insertions(+), 139 deletions(-)
>
> --
> 2.31.1

2021-10-15 03:22:46

by Daniel Axtens

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'

Christophe Leroy <[email protected]> writes:

> There are three architectures with function descriptors, try to
> have common names for the address they contain in order to
> refactor some functions into generic functions later.
>
> powerpc has 'funcaddr'
> ia64 has 'ip'
> parisc has 'addr'
>
> Vote for 'addr' and update 'struct ppc64_opd_entry' accordingly.

I would have picked 'funcaddr', but at least 'addr' is better than 'ip'!
And I agree that consistency, and then making things generic is worthwhile.

I grepped the latest powerpc/next for uses of 'funcaddr'. There were 5,
your patch changes all 5.

The series passes build tests and this patch has no checkpatch or other
style concerns.

On that basis:
Reviewed-by: Daniel Axtens <[email protected]>

Kind regards,
Daniel

> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> arch/powerpc/include/asm/elf.h | 2 +-
> arch/powerpc/include/asm/sections.h | 2 +-
> arch/powerpc/kernel/module_64.c | 6 +++---
> 3 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
> index a4406714c060..bb0f278f9ed4 100644
> --- a/arch/powerpc/include/asm/elf.h
> +++ b/arch/powerpc/include/asm/elf.h
> @@ -178,7 +178,7 @@ void relocate(unsigned long final_address);
>
> /* There's actually a third entry here, but it's unused */
> struct ppc64_opd_entry {
> - unsigned long funcaddr;
> + unsigned long addr;
> unsigned long r2;
> };
>
> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
> index 6e4af4492a14..32e7035863ac 100644
> --- a/arch/powerpc/include/asm/sections.h
> +++ b/arch/powerpc/include/asm/sections.h
> @@ -77,7 +77,7 @@ static inline void *dereference_function_descriptor(void *ptr)
> struct ppc64_opd_entry *desc = ptr;
> void *p;
>
> - if (!get_kernel_nofault(p, (void *)&desc->funcaddr))
> + if (!get_kernel_nofault(p, (void *)&desc->addr))
> ptr = p;
> return ptr;
> }
> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
> index 6baa676e7cb6..82908c9be627 100644
> --- a/arch/powerpc/kernel/module_64.c
> +++ b/arch/powerpc/kernel/module_64.c
> @@ -72,11 +72,11 @@ static func_desc_t func_desc(unsigned long addr)
> }
> static unsigned long func_addr(unsigned long addr)
> {
> - return func_desc(addr).funcaddr;
> + return func_desc(addr).addr;
> }
> static unsigned long stub_func_addr(func_desc_t func)
> {
> - return func.funcaddr;
> + return func.addr;
> }
> static unsigned int local_entry_offset(const Elf64_Sym *sym)
> {
> @@ -187,7 +187,7 @@ static int relacmp(const void *_x, const void *_y)
> static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
> 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 i;
>
> --
> 2.31.1

2021-10-15 04:06:37

by Daniel Axtens

[permalink] [raw]
Subject: Re: [PATCH v2 03/13] powerpc: Remove func_descr_t

Christophe Leroy <[email protected]> writes:

> 'func_descr_t' is redundant with 'struct ppc64_opd_entry'

So, if I understand the overall direction of the series, you're
consolidating powerpc around one single type for function descriptors,
and then you're creating a generic typedef so that generic code can
always do ((func_desc_t)x)->addr to get the address of a function out of
a function descriptor regardless of arch. (And regardless of whether the
arch uses function descriptors or not.)

So:

- why pick ppc64_opd_entry over func_descr_t?

- Why not make our struct just called func_desc_t - why have a
ppc64_opd_entry type or a func_descr_t typedef?

- Should this patch wait until after you've made the generic
func_desc_t change and move directly to that new interface? (rather
than move from func_descr_t -> ppc64_opd_entry -> ...) Or is there a
particular reason arch specific code should use an arch-specific
struct or named type?

> Remove it.
>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> arch/powerpc/include/asm/code-patching.h | 2 +-
> arch/powerpc/include/asm/types.h | 6 ------
> arch/powerpc/kernel/signal_64.c | 8 ++++----
> 3 files changed, 5 insertions(+), 11 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
> index 4ba834599c4d..f3445188d319 100644
> --- a/arch/powerpc/include/asm/code-patching.h
> +++ b/arch/powerpc/include/asm/code-patching.h
> @@ -110,7 +110,7 @@ static inline unsigned long ppc_function_entry(void *func)
> * function's descriptor. The first entry in the descriptor is the
> * address of the function text.
> */
> - return ((func_descr_t *)func)->entry;
> + return ((struct ppc64_opd_entry *)func)->addr;
> #else
> return (unsigned long)func;
> #endif
> diff --git a/arch/powerpc/include/asm/types.h b/arch/powerpc/include/asm/types.h
> index f1630c553efe..97da77bc48c9 100644
> --- a/arch/powerpc/include/asm/types.h
> +++ b/arch/powerpc/include/asm/types.h
> @@ -23,12 +23,6 @@
>
> typedef __vector128 vector128;
>
> -typedef struct {
> - unsigned long entry;
> - unsigned long toc;
> - unsigned long env;
> -} func_descr_t;

I was a little concerned about going from a 3-element struct to a
2-element struct (as ppc64_opd_entry doesn't have an element for env) -
but we don't seem to take the sizeof this anywhere, nor do we use env
anywhere, nor do we do funky macro stuff with it in the signal handling
code that might implictly use the 3rd element, so I guess this will
work. Still, func_descr_t seems to describe the underlying ABI better
than ppc64_opd_entry...

> #endif /* __ASSEMBLY__ */
>
> #endif /* _ASM_POWERPC_TYPES_H */
> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> index 1831bba0582e..63ddbe7b108c 100644
> --- a/arch/powerpc/kernel/signal_64.c
> +++ b/arch/powerpc/kernel/signal_64.c
> @@ -933,11 +933,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
> * descriptor is the entry address of signal and the second
> * entry is the TOC value we need to use.
> */
> - func_descr_t __user *funct_desc_ptr =
> - (func_descr_t __user *) ksig->ka.sa.sa_handler;
> + struct ppc64_opd_entry __user *funct_desc_ptr =
> + (struct ppc64_opd_entry __user *)ksig->ka.sa.sa_handler;
>
> - err |= get_user(regs->ctr, &funct_desc_ptr->entry);
> - err |= get_user(regs->gpr[2], &funct_desc_ptr->toc);
> + err |= get_user(regs->ctr, &funct_desc_ptr->addr);
> + err |= get_user(regs->gpr[2], &funct_desc_ptr->r2);

Likewise, r2 seems like a worse name than toc. I guess we could clean
that up another time though.

Kind regards,
Daniel

> }
>
> /* enter the signal handler in native-endian mode */
> --
> 2.31.1

2021-10-15 12:09:41

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'



Le 14/10/2021 à 23:45, Daniel Axtens a écrit :
> Christophe Leroy <[email protected]> writes:
>
>> There are three architectures with function descriptors, try to
>> have common names for the address they contain in order to
>> refactor some functions into generic functions later.
>>
>> powerpc has 'funcaddr'
>> ia64 has 'ip'
>> parisc has 'addr'
>>
>> Vote for 'addr' and update 'struct ppc64_opd_entry' accordingly.
>
> I would have picked 'funcaddr', but at least 'addr' is better than 'ip'!
> And I agree that consistency, and then making things generic is worthwhile.

It's a function descriptor, there is only one address field, I don't
think there is any ambiguïty here, and I prefer modifying the least
impacted architectures.

Changing addr to funcaddr in PARISC would result in the following
changes, on an architecture I know nothing about. It's more changes than
we have on powerpc.

arch/parisc/include/asm/elf.h | 4 ++--
arch/parisc/kernel/kexec.c | 2 +-
arch/parisc/kernel/module.c | 12 ++++++------
arch/parisc/kernel/process.c | 2 +-
arch/parisc/kernel/signal.c | 4 ++--
5 files changed, 12 insertions(+), 12 deletions(-)



>
> I grepped the latest powerpc/next for uses of 'funcaddr'. There were 5,
> your patch changes all 5.
>
> The series passes build tests and this patch has no checkpatch or other
> style concerns.
>
> On that basis:
> Reviewed-by: Daniel Axtens <[email protected]>
>
> Kind regards,
> Daniel
>
>> Reviewed-by: Kees Cook <[email protected]>
>> Signed-off-by: Christophe Leroy <[email protected]>
>> ---
>> arch/powerpc/include/asm/elf.h | 2 +-
>> arch/powerpc/include/asm/sections.h | 2 +-
>> arch/powerpc/kernel/module_64.c | 6 +++---
>> 3 files changed, 5 insertions(+), 5 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
>> index a4406714c060..bb0f278f9ed4 100644
>> --- a/arch/powerpc/include/asm/elf.h
>> +++ b/arch/powerpc/include/asm/elf.h
>> @@ -178,7 +178,7 @@ void relocate(unsigned long final_address);
>>
>> /* There's actually a third entry here, but it's unused */
>> struct ppc64_opd_entry {
>> - unsigned long funcaddr;
>> + unsigned long addr;
>> unsigned long r2;
>> };
>>
>> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
>> index 6e4af4492a14..32e7035863ac 100644
>> --- a/arch/powerpc/include/asm/sections.h
>> +++ b/arch/powerpc/include/asm/sections.h
>> @@ -77,7 +77,7 @@ static inline void *dereference_function_descriptor(void *ptr)
>> struct ppc64_opd_entry *desc = ptr;
>> void *p;
>>
>> - if (!get_kernel_nofault(p, (void *)&desc->funcaddr))
>> + if (!get_kernel_nofault(p, (void *)&desc->addr))
>> ptr = p;
>> return ptr;
>> }
>> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
>> index 6baa676e7cb6..82908c9be627 100644
>> --- a/arch/powerpc/kernel/module_64.c
>> +++ b/arch/powerpc/kernel/module_64.c
>> @@ -72,11 +72,11 @@ static func_desc_t func_desc(unsigned long addr)
>> }
>> static unsigned long func_addr(unsigned long addr)
>> {
>> - return func_desc(addr).funcaddr;
>> + return func_desc(addr).addr;
>> }
>> static unsigned long stub_func_addr(func_desc_t func)
>> {
>> - return func.funcaddr;
>> + return func.addr;
>> }
>> static unsigned int local_entry_offset(const Elf64_Sym *sym)
>> {
>> @@ -187,7 +187,7 @@ static int relacmp(const void *_x, const void *_y)
>> static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
>> 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 i;
>>
>> --
>> 2.31.1

2021-10-15 12:13:44

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 03/13] powerpc: Remove func_descr_t



Le 15/10/2021 à 00:17, Daniel Axtens a écrit :
> Christophe Leroy <[email protected]> writes:
>
>> 'func_descr_t' is redundant with 'struct ppc64_opd_entry'
>
> So, if I understand the overall direction of the series, you're
> consolidating powerpc around one single type for function descriptors,
> and then you're creating a generic typedef so that generic code can
> always do ((func_desc_t)x)->addr to get the address of a function out of
> a function descriptor regardless of arch. (And regardless of whether the
> arch uses function descriptors or not.)

An architecture not using function descriptors won't do much with
((func_desc_t *)x)->addr. This is just done to allow building stuff
regardless.

I prefer something like

if (have_function_descriptors())
addr = (func_desc_t *)ptr)->addr;
else
addr = ptr;

over

#ifdef HAVE_FUNCTION_DESCRIPTORS
addr = (func_desc_t *)ptr)->addr;
#else
addr = ptr;
#endif

>
> So:
>
> - why pick ppc64_opd_entry over func_descr_t?

Good question. At the begining it was because it was in UAPI headers,
and also because it was the one used in our
dereference_function_descriptor().

But at the end maybe that's not the more logical choice. I need to look
a bit more.

>
> - Why not make our struct just called func_desc_t - why have a
> ppc64_opd_entry type or a func_descr_t typedef?

Well ... you usually don't flag a struct name with _t, _t will most of
the time refer to a typedef.

If I want to avoid typedef (I know they are deprecated in kernel coding
stype), it means the name of the struct must be changed in every
architecture and it becomes tricky and it adds more churn in them, which
is what I want to avoid.

At the end we risk to end-up with a messy set of #ifdefs.

Maybe this can be done as a second step, but I would like to minimise
impact in this series and focus on fixing lkdtm.


>
> - Should this patch wait until after you've made the generic
> func_desc_t change and move directly to that new interface? (rather
> than move from func_descr_t -> ppc64_opd_entry -> ...) Or is there a
> particular reason arch specific code should use an arch-specific
> struct or named type?

As mentioned in kernel coding style, typedefs reduce readability, see
https://www.kernel.org/doc/html/latest/process/coding-style.html#typedefs

But yes, we could make a step in the direction of a common 'struct
func_desc'. Let's see if I can do that.

Thanks for your comments.

Christophe

>
>> Remove it.
>>
>> Signed-off-by: Christophe Leroy <[email protected]>
>> ---
>> arch/powerpc/include/asm/code-patching.h | 2 +-
>> arch/powerpc/include/asm/types.h | 6 ------
>> arch/powerpc/kernel/signal_64.c | 8 ++++----
>> 3 files changed, 5 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
>> index 4ba834599c4d..f3445188d319 100644
>> --- a/arch/powerpc/include/asm/code-patching.h
>> +++ b/arch/powerpc/include/asm/code-patching.h
>> @@ -110,7 +110,7 @@ static inline unsigned long ppc_function_entry(void *func)
>> * function's descriptor. The first entry in the descriptor is the
>> * address of the function text.
>> */
>> - return ((func_descr_t *)func)->entry;
>> + return ((struct ppc64_opd_entry *)func)->addr;
>> #else
>> return (unsigned long)func;
>> #endif
>> diff --git a/arch/powerpc/include/asm/types.h b/arch/powerpc/include/asm/types.h
>> index f1630c553efe..97da77bc48c9 100644
>> --- a/arch/powerpc/include/asm/types.h
>> +++ b/arch/powerpc/include/asm/types.h
>> @@ -23,12 +23,6 @@
>>
>> typedef __vector128 vector128;
>>
>> -typedef struct {
>> - unsigned long entry;
>> - unsigned long toc;
>> - unsigned long env;
>> -} func_descr_t;
>
> I was a little concerned about going from a 3-element struct to a
> 2-element struct (as ppc64_opd_entry doesn't have an element for env) -
> but we don't seem to take the sizeof this anywhere, nor do we use env
> anywhere, nor do we do funky macro stuff with it in the signal handling
> code that might implictly use the 3rd element, so I guess this will
> work. Still, func_descr_t seems to describe the underlying ABI better
> than ppc64_opd_entry...
>
>> #endif /* __ASSEMBLY__ */
>>
>> #endif /* _ASM_POWERPC_TYPES_H */
>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
>> index 1831bba0582e..63ddbe7b108c 100644
>> --- a/arch/powerpc/kernel/signal_64.c
>> +++ b/arch/powerpc/kernel/signal_64.c
>> @@ -933,11 +933,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
>> * descriptor is the entry address of signal and the second
>> * entry is the TOC value we need to use.
>> */
>> - func_descr_t __user *funct_desc_ptr =
>> - (func_descr_t __user *) ksig->ka.sa.sa_handler;
>> + struct ppc64_opd_entry __user *funct_desc_ptr =
>> + (struct ppc64_opd_entry __user *)ksig->ka.sa.sa_handler;
>>
>> - err |= get_user(regs->ctr, &funct_desc_ptr->entry);
>> - err |= get_user(regs->gpr[2], &funct_desc_ptr->toc);
>> + err |= get_user(regs->ctr, &funct_desc_ptr->addr);
>> + err |= get_user(regs->gpr[2], &funct_desc_ptr->r2);
>
> Likewise, r2 seems like a worse name than toc. I guess we could clean
> that up another time though.
>
> Kind regards,
> Daniel
>
>> }
>>
>> /* enter the signal handler in native-endian mode */
>> --
>> 2.31.1

2021-10-15 12:47:15

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 03/13] powerpc: Remove func_descr_t

Excerpts from Christophe Leroy's message of October 15, 2021 3:19 pm:
>
>
> Le 15/10/2021 à 00:17, Daniel Axtens a écrit :
>> Christophe Leroy <[email protected]> writes:
>>
>>> 'func_descr_t' is redundant with 'struct ppc64_opd_entry'
>>
>> So, if I understand the overall direction of the series, you're
>> consolidating powerpc around one single type for function descriptors,
>> and then you're creating a generic typedef so that generic code can
>> always do ((func_desc_t)x)->addr to get the address of a function out of
>> a function descriptor regardless of arch. (And regardless of whether the
>> arch uses function descriptors or not.)
>
> An architecture not using function descriptors won't do much with
> ((func_desc_t *)x)->addr. This is just done to allow building stuff
> regardless.
>
> I prefer something like
>
> if (have_function_descriptors())
> addr = (func_desc_t *)ptr)->addr;
> else
> addr = ptr;

If you make a generic data type for architectures without function
descriptors as such

typedef struct func_desc {
char addr[0];
} func_desc_t;

Then you can do that with no if. The downside is your addr has to be
char * and it's maybe not helpful to be so "clever".

>> - why pick ppc64_opd_entry over func_descr_t?
>
> Good question. At the begining it was because it was in UAPI headers,
> and also because it was the one used in our
> dereference_function_descriptor().
>
> But at the end maybe that's not the more logical choice. I need to look
> a bit more.

I would prefer the func_descr_t (with 'toc' and 'env') if you're going
to change it.

Thanks,
Nick

2021-10-15 12:47:15

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 06/13] asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs

Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
> Replace HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR by
> HAVE_FUNCTION_DESCRIPTORS and use it instead of
> 'dereference_function_descriptor' macro to know
> whether an arch has function descriptors.
>
> To limit churn in one of the following patches, use
> an #ifdef/#else construct with empty first part
> instead of an #ifndef in asm-generic/sections.h

Is it worth putting this into Kconfig if you're going to
change it? In any case

Reviewed-by: Nicholas Piggin <[email protected]>

>
> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> arch/ia64/include/asm/sections.h | 5 +++--
> arch/parisc/include/asm/sections.h | 6 ++++--
> arch/powerpc/include/asm/sections.h | 6 ++++--
> include/asm-generic/sections.h | 3 ++-
> include/linux/kallsyms.h | 2 +-
> 5 files changed, 14 insertions(+), 8 deletions(-)
>
> diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
> index 35f24e52149a..6e55e545bf02 100644
> --- a/arch/ia64/include/asm/sections.h
> +++ b/arch/ia64/include/asm/sections.h
> @@ -9,6 +9,9 @@
>
> #include <linux/elf.h>
> #include <linux/uaccess.h>
> +
> +#define HAVE_FUNCTION_DESCRIPTORS 1
> +
> #include <asm-generic/sections.h>
>
> extern char __phys_per_cpu_start[];
> @@ -27,8 +30,6 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
> extern char __start_unwind[], __end_unwind[];
> 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)
> {
> diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
> index bb52aea0cb21..85149a89ff3e 100644
> --- a/arch/parisc/include/asm/sections.h
> +++ b/arch/parisc/include/asm/sections.h
> @@ -2,6 +2,10 @@
> #ifndef _PARISC_SECTIONS_H
> #define _PARISC_SECTIONS_H
>
> +#ifdef CONFIG_64BIT
> +#define HAVE_FUNCTION_DESCRIPTORS 1
> +#endif
> +
> /* nothing to see, move along */
> #include <asm-generic/sections.h>
>
> @@ -9,8 +13,6 @@ 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 *);
>
> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
> index 32e7035863ac..bba97b8c38cf 100644
> --- a/arch/powerpc/include/asm/sections.h
> +++ b/arch/powerpc/include/asm/sections.h
> @@ -8,6 +8,10 @@
>
> #define arch_is_kernel_initmem_freed arch_is_kernel_initmem_freed
>
> +#ifdef PPC64_ELF_ABI_v1
> +#define HAVE_FUNCTION_DESCRIPTORS 1
> +#endif
> +
> #include <asm-generic/sections.h>
>
> extern bool init_mem_is_free;
> @@ -69,8 +73,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)
>
> #ifdef PPC64_ELF_ABI_v1
>
> -#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
> -
> #undef dereference_function_descriptor
> static inline void *dereference_function_descriptor(void *ptr)
> {
> diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
> index d16302d3eb59..b677e926e6b3 100644
> --- a/include/asm-generic/sections.h
> +++ b/include/asm-generic/sections.h
> @@ -59,7 +59,8 @@ extern char __noinstr_text_start[], __noinstr_text_end[];
> extern __visible const void __nosave_begin, __nosave_end;
>
> /* Function descriptor handling (if any). Override in asm/sections.h */
> -#ifndef dereference_function_descriptor
> +#ifdef HAVE_FUNCTION_DESCRIPTORS
> +#else
> #define dereference_function_descriptor(p) ((void *)(p))
> #define dereference_kernel_function_descriptor(p) ((void *)(p))
> #endif
> diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
> index a1d6fc82d7f0..9f277baeb559 100644
> --- a/include/linux/kallsyms.h
> +++ b/include/linux/kallsyms.h
> @@ -57,7 +57,7 @@ static inline int is_ksym_addr(unsigned long addr)
>
> static inline void *dereference_symbol_descriptor(void *ptr)
> {
> -#ifdef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR
> +#ifdef HAVE_FUNCTION_DESCRIPTORS
> struct module *mod;
>
> ptr = dereference_kernel_function_descriptor(ptr);
> --
> 2.31.1
>
>
>

2021-10-15 12:47:15

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'

Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
> There are three architectures with function descriptors, try to
> have common names for the address they contain in order to
> refactor some functions into generic functions later.
>
> powerpc has 'funcaddr'
> ia64 has 'ip'
> parisc has 'addr'
>
> Vote for 'addr' and update 'struct ppc64_opd_entry' accordingly.

It is the "address of the entry point of the function" according to
powerpc ELF spec, so addr seems fine.

Reviewed-by: Nicholas Piggin <[email protected]>

>
> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> arch/powerpc/include/asm/elf.h | 2 +-
> arch/powerpc/include/asm/sections.h | 2 +-
> arch/powerpc/kernel/module_64.c | 6 +++---
> 3 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
> index a4406714c060..bb0f278f9ed4 100644
> --- a/arch/powerpc/include/asm/elf.h
> +++ b/arch/powerpc/include/asm/elf.h
> @@ -178,7 +178,7 @@ void relocate(unsigned long final_address);
>
> /* There's actually a third entry here, but it's unused */
> struct ppc64_opd_entry {
> - unsigned long funcaddr;
> + unsigned long addr;
> unsigned long r2;
> };
>
> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
> index 6e4af4492a14..32e7035863ac 100644
> --- a/arch/powerpc/include/asm/sections.h
> +++ b/arch/powerpc/include/asm/sections.h
> @@ -77,7 +77,7 @@ static inline void *dereference_function_descriptor(void *ptr)
> struct ppc64_opd_entry *desc = ptr;
> void *p;
>
> - if (!get_kernel_nofault(p, (void *)&desc->funcaddr))
> + if (!get_kernel_nofault(p, (void *)&desc->addr))
> ptr = p;
> return ptr;
> }
> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
> index 6baa676e7cb6..82908c9be627 100644
> --- a/arch/powerpc/kernel/module_64.c
> +++ b/arch/powerpc/kernel/module_64.c
> @@ -72,11 +72,11 @@ static func_desc_t func_desc(unsigned long addr)
> }
> static unsigned long func_addr(unsigned long addr)
> {
> - return func_desc(addr).funcaddr;
> + return func_desc(addr).addr;
> }
> static unsigned long stub_func_addr(func_desc_t func)
> {
> - return func.funcaddr;
> + return func.addr;
> }
> static unsigned int local_entry_offset(const Elf64_Sym *sym)
> {
> @@ -187,7 +187,7 @@ static int relacmp(const void *_x, const void *_y)
> static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
> 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 i;
>
> --
> 2.31.1
>
>
>

2021-10-15 12:47:37

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 06/13] asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs



Le 15/10/2021 à 08:16, Nicholas Piggin a écrit :
> Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
>> Replace HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR by
>> HAVE_FUNCTION_DESCRIPTORS and use it instead of
>> 'dereference_function_descriptor' macro to know
>> whether an arch has function descriptors.
>>
>> To limit churn in one of the following patches, use
>> an #ifdef/#else construct with empty first part
>> instead of an #ifndef in asm-generic/sections.h
>
> Is it worth putting this into Kconfig if you're going to
> change it? In any case

That was what I wanted to do in the begining but how can I do that in
Kconfig ?

#ifdef __powerpc64__
#if defined(_CALL_ELF) && _CALL_ELF == 2
#define PPC64_ELF_ABI_v2
#else
#define PPC64_ELF_ABI_v1
#endif
#endif /* __powerpc64__ */

#ifdef PPC64_ELF_ABI_v1
#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1

Christophe

>
> Reviewed-by: Nicholas Piggin <[email protected]>
>
>>
>> Reviewed-by: Kees Cook <[email protected]>
>> Signed-off-by: Christophe Leroy <[email protected]>
>> ---
>> arch/ia64/include/asm/sections.h | 5 +++--
>> arch/parisc/include/asm/sections.h | 6 ++++--
>> arch/powerpc/include/asm/sections.h | 6 ++++--
>> include/asm-generic/sections.h | 3 ++-
>> include/linux/kallsyms.h | 2 +-
>> 5 files changed, 14 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
>> index 35f24e52149a..6e55e545bf02 100644
>> --- a/arch/ia64/include/asm/sections.h
>> +++ b/arch/ia64/include/asm/sections.h
>> @@ -9,6 +9,9 @@
>>
>> #include <linux/elf.h>
>> #include <linux/uaccess.h>
>> +
>> +#define HAVE_FUNCTION_DESCRIPTORS 1
>> +
>> #include <asm-generic/sections.h>
>>
>> extern char __phys_per_cpu_start[];
>> @@ -27,8 +30,6 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
>> extern char __start_unwind[], __end_unwind[];
>> 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)
>> {
>> diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
>> index bb52aea0cb21..85149a89ff3e 100644
>> --- a/arch/parisc/include/asm/sections.h
>> +++ b/arch/parisc/include/asm/sections.h
>> @@ -2,6 +2,10 @@
>> #ifndef _PARISC_SECTIONS_H
>> #define _PARISC_SECTIONS_H
>>
>> +#ifdef CONFIG_64BIT
>> +#define HAVE_FUNCTION_DESCRIPTORS 1
>> +#endif
>> +
>> /* nothing to see, move along */
>> #include <asm-generic/sections.h>
>>
>> @@ -9,8 +13,6 @@ 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 *);
>>
>> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
>> index 32e7035863ac..bba97b8c38cf 100644
>> --- a/arch/powerpc/include/asm/sections.h
>> +++ b/arch/powerpc/include/asm/sections.h
>> @@ -8,6 +8,10 @@
>>
>> #define arch_is_kernel_initmem_freed arch_is_kernel_initmem_freed
>>
>> +#ifdef PPC64_ELF_ABI_v1
>> +#define HAVE_FUNCTION_DESCRIPTORS 1
>> +#endif
>> +
>> #include <asm-generic/sections.h>
>>
>> extern bool init_mem_is_free;
>> @@ -69,8 +73,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)
>>
>> #ifdef PPC64_ELF_ABI_v1
>>
>> -#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
>> -
>> #undef dereference_function_descriptor
>> static inline void *dereference_function_descriptor(void *ptr)
>> {
>> diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
>> index d16302d3eb59..b677e926e6b3 100644
>> --- a/include/asm-generic/sections.h
>> +++ b/include/asm-generic/sections.h
>> @@ -59,7 +59,8 @@ extern char __noinstr_text_start[], __noinstr_text_end[];
>> extern __visible const void __nosave_begin, __nosave_end;
>>
>> /* Function descriptor handling (if any). Override in asm/sections.h */
>> -#ifndef dereference_function_descriptor
>> +#ifdef HAVE_FUNCTION_DESCRIPTORS
>> +#else
>> #define dereference_function_descriptor(p) ((void *)(p))
>> #define dereference_kernel_function_descriptor(p) ((void *)(p))
>> #endif
>> diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
>> index a1d6fc82d7f0..9f277baeb559 100644
>> --- a/include/linux/kallsyms.h
>> +++ b/include/linux/kallsyms.h
>> @@ -57,7 +57,7 @@ static inline int is_ksym_addr(unsigned long addr)
>>
>> static inline void *dereference_symbol_descriptor(void *ptr)
>> {
>> -#ifdef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR
>> +#ifdef HAVE_FUNCTION_DESCRIPTORS
>> struct module *mod;
>>
>> ptr = dereference_kernel_function_descriptor(ptr);
>> --
>> 2.31.1
>>
>>
>>

2021-10-15 13:20:16

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 08/13] asm-generic: Refactor dereference_[kernel]_function_descriptor()

Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
> dereference_function_descriptor() and
> dereference_kernel_function_descriptor() are identical on the
> three architectures implementing them.
>
> Make them common and put them out-of-line in kernel/extable.c
> which is one of the users and has similar type of functions.

We should be moving more stuff out of extable.c (including all the
kernel address tests). lib/kimage.c or kelf.c or something.

It could be after your series though.

>
> Reviewed-by: Kees Cook <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> arch/ia64/include/asm/sections.h | 19 -------------------
> arch/parisc/include/asm/sections.h | 9 ---------
> arch/parisc/kernel/process.c | 21 ---------------------
> arch/powerpc/include/asm/sections.h | 23 -----------------------
> include/asm-generic/sections.h | 2 ++
> kernel/extable.c | 23 ++++++++++++++++++++++-
> 6 files changed, 24 insertions(+), 73 deletions(-)
>
> diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h
> index 1aaed8882294..96c9bb500c34 100644
> --- a/arch/ia64/include/asm/sections.h
> +++ b/arch/ia64/include/asm/sections.h
> @@ -31,23 +31,4 @@ extern char __start_gate_brl_fsys_bubble_down_patchlist[], __end_gate_brl_fsys_b
> extern char __start_unwind[], __end_unwind[];
> extern char __start_ivt_text[], __end_ivt_text[];
>
> -#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->addr))
> - 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 */
> diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
> index 37b34b357cb5..6b1fe22baaf5 100644
> --- a/arch/parisc/include/asm/sections.h
> +++ b/arch/parisc/include/asm/sections.h
> @@ -13,13 +13,4 @@ typedef Elf64_Fdesc func_desc_t;
>
> extern char __alt_instructions[], __alt_instructions_end[];
>
> -#ifdef CONFIG_64BIT
> -
> -#undef dereference_function_descriptor
> -void *dereference_function_descriptor(void *);
> -
> -#undef dereference_kernel_function_descriptor
> -void *dereference_kernel_function_descriptor(void *);
> -#endif
> -
> #endif
> diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
> index 38ec4ae81239..7382576b52a8 100644
> --- a/arch/parisc/kernel/process.c
> +++ b/arch/parisc/kernel/process.c
> @@ -266,27 +266,6 @@ get_wchan(struct task_struct *p)
> 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)
> {
> return (get_random_int() & BRK_RND_MASK) << PAGE_SHIFT;
> diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h
> index 1322d7b2f1a3..fbfe1957edbe 100644
> --- a/arch/powerpc/include/asm/sections.h
> +++ b/arch/powerpc/include/asm/sections.h
> @@ -72,29 +72,6 @@ static inline int overlaps_kernel_text(unsigned long start, unsigned long end)
> (unsigned long)_stext < end;
> }
>
> -#ifdef PPC64_ELF_ABI_v1
> -
> -#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->addr))
> - 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 /* __KERNEL__ */
> diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
> index cbec7d5f1678..76163883c6ff 100644
> --- a/include/asm-generic/sections.h
> +++ b/include/asm-generic/sections.h
> @@ -60,6 +60,8 @@ extern __visible const void __nosave_begin, __nosave_end;
>
> /* Function descriptor handling (if any). Override in asm/sections.h */
> #ifdef 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_kernel_function_descriptor(p) ((void *)(p))
> diff --git a/kernel/extable.c b/kernel/extable.c
> index b0ea5eb0c3b4..013ccffade11 100644
> --- a/kernel/extable.c
> +++ b/kernel/extable.c
> @@ -3,6 +3,7 @@
> Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
>
> */
> +#include <linux/elf.h>
> #include <linux/ftrace.h>
> #include <linux/memory.h>
> #include <linux/extable.h>
> @@ -159,12 +160,32 @@ 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
> * real function address. As a result, to find if a function
> * pointer is part of the kernel text, we need to do some
> * special dereferencing first.
> */
> +#ifdef 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;

I know you're just copying existing code. This seems a bit risky though.
I don't think anything good could come of just treating the descriptor
address like a function entry address if we failed to load from it for
whatever reason.

Existing callers might be benign but the API is not good. It should
give a nice fail return or BUG. If we change that then we should also
change the name and pass the correct type to it too.

Thanks,
Nick

2021-10-15 13:55:26

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 06/13] asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs

Excerpts from Christophe Leroy's message of October 15, 2021 4:24 pm:
>
>
> Le 15/10/2021 à 08:16, Nicholas Piggin a écrit :
>> Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
>>> Replace HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR by
>>> HAVE_FUNCTION_DESCRIPTORS and use it instead of
>>> 'dereference_function_descriptor' macro to know
>>> whether an arch has function descriptors.
>>>
>>> To limit churn in one of the following patches, use
>>> an #ifdef/#else construct with empty first part
>>> instead of an #ifndef in asm-generic/sections.h
>>
>> Is it worth putting this into Kconfig if you're going to
>> change it? In any case
>
> That was what I wanted to do in the begining but how can I do that in
> Kconfig ?
>
> #ifdef __powerpc64__
> #if defined(_CALL_ELF) && _CALL_ELF == 2
> #define PPC64_ELF_ABI_v2
> #else
> #define PPC64_ELF_ABI_v1
> #endif
> #endif /* __powerpc64__ */
>
> #ifdef PPC64_ELF_ABI_v1
> #define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1

We have ELFv2 ABI / function descriptors iff big-endian so you could
just select based on that.

I have a patch that makes the ABI version configurable which cleans
some of this up a bit, but that can be rebased on your series if we
ever merge it. Maybe just add BUILD_BUG_ONs in the above ifdef block
to ensure CONFIG_HAVE_FUNCTION_DESCRIPTORS was set the right way, so
I don't forget.

Thanks,
Nick

2021-10-15 21:41:35

by Nicholas Piggin

[permalink] [raw]
Subject: Re: [PATCH v2 06/13] asm-generic: Use HAVE_FUNCTION_DESCRIPTORS to define associated stubs

Excerpts from Nicholas Piggin's message of October 15, 2021 6:02 pm:
> Excerpts from Christophe Leroy's message of October 15, 2021 4:24 pm:
>>
>>
>> Le 15/10/2021 à 08:16, Nicholas Piggin a écrit :
>>> Excerpts from Christophe Leroy's message of October 14, 2021 3:49 pm:
>>>> Replace HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR by
>>>> HAVE_FUNCTION_DESCRIPTORS and use it instead of
>>>> 'dereference_function_descriptor' macro to know
>>>> whether an arch has function descriptors.
>>>>
>>>> To limit churn in one of the following patches, use
>>>> an #ifdef/#else construct with empty first part
>>>> instead of an #ifndef in asm-generic/sections.h
>>>
>>> Is it worth putting this into Kconfig if you're going to
>>> change it? In any case
>>
>> That was what I wanted to do in the begining but how can I do that in
>> Kconfig ?
>>
>> #ifdef __powerpc64__
>> #if defined(_CALL_ELF) && _CALL_ELF == 2
>> #define PPC64_ELF_ABI_v2
>> #else
>> #define PPC64_ELF_ABI_v1
>> #endif
>> #endif /* __powerpc64__ */
>>
>> #ifdef PPC64_ELF_ABI_v1
>> #define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
>
> We have ELFv2 ABI / function descriptors iff big-endian so you could
> just select based on that.

Of course that should read ELFv1. To be clearer: BE is ELFv1 ABI and
LE is ELFv2 ABI.

Thanks,
Nick

2021-10-18 03:07:12

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v2 12/13] lkdtm: Fix execute_[user]_location()

On Thu, Oct 14, 2021 at 07:50:01AM +0200, Christophe Leroy wrote:
> execute_location() and execute_user_location() intent
> to copy do_nothing() text and execute it at a new location.
> However, at the time being it doesn't copy do_nothing() function
> but do_nothing() function descriptor which still points to the
> original text. So at the end it still executes do_nothing() at
> its original location allthough using a copied function descriptor.
>
> So, fix that by really copying do_nothing() text and build a new
> function descriptor by copying do_nothing() function descriptor and
> updating the target address with the new location.
>
> Also fix the displayed addresses by dereferencing do_nothing()
> function descriptor.
>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> drivers/misc/lkdtm/perms.c | 25 +++++++++++++++++++++----
> include/asm-generic/sections.h | 5 +++++
> 2 files changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
> index 5266dc28df6e..96b3ebfcb8ed 100644
> --- a/drivers/misc/lkdtm/perms.c
> +++ b/drivers/misc/lkdtm/perms.c
> @@ -44,19 +44,32 @@ static noinline void do_overwritten(void)
> return;
> }
>
> +static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
> +{
> + memcpy(fdesc, do_nothing, sizeof(*fdesc));
> + fdesc->addr = (unsigned long)dst;
> + barrier();
> +
> + return fdesc;
> +}

How about collapsing the "have_function_descriptors()" check into
setup_function_descriptor()?

static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
{
if (__is_defined(HAVE_FUNCTION_DESCRIPTORS)) {
memcpy(fdesc, do_nothing, sizeof(*fdesc));
fdesc->addr = (unsigned long)dst;
barrier();
return fdesc;
} else {
return dst;
}
}

> +
> static noinline void execute_location(void *dst, bool write)
> {
> void (*func)(void) = dst;
> + 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();
>
> if (write == CODE_WRITE) {
> - memcpy(dst, do_nothing, EXEC_SIZE);
> + memcpy(dst, do_nothing_text, EXEC_SIZE);
> flush_icache_range((unsigned long)dst,
> (unsigned long)dst + EXEC_SIZE);
> }
> pr_info("attempting bad execution at %px\n", func);
> + if (have_function_descriptors())
> + func = setup_function_descriptor(&fdesc, dst);
> func();
> pr_err("FAIL: func returned\n");
> }
> @@ -67,15 +80,19 @@ static void execute_user_location(void *dst)
>
> /* Intentionally crossing kernel/user memory boundary. */
> void (*func)(void) = dst;
> + 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();
>
> - 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);
> if (copied < EXEC_SIZE)
> return;
> pr_info("attempting bad execution at %px\n", func);
> + if (have_function_descriptors())
> + func = setup_function_descriptor(&fdesc, dst);
> func();
> pr_err("FAIL: func returned\n");
> }


> diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
> index 76163883c6ff..d225318538bd 100644
> --- a/include/asm-generic/sections.h
> +++ b/include/asm-generic/sections.h
> @@ -70,6 +70,11 @@ typedef struct {
> } func_desc_t;
> #endif
>
> +static inline bool have_function_descriptors(void)
> +{
> + return __is_defined(HAVE_FUNCTION_DESCRIPTORS);
> +}
> +
> /* random extra sections (if any). Override
> * in asm/sections.h */
> #ifndef arch_is_kernel_text

This hunk seems like it should live in a separate patch.

--
Kees Cook

2021-10-18 03:07:37

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v2 13/13] lkdtm: Add a test for function descriptors protection

On Thu, Oct 14, 2021 at 07:50:02AM +0200, Christophe Leroy wrote:
> Add WRITE_OPD to check that you can't modify function
> descriptors.
>
> Gives the following result when function descriptors are
> not protected:
>
> lkdtm: Performing direct entry WRITE_OPD
> lkdtm: attempting bad 16 bytes write at c00000000269b358
> lkdtm: FAIL: survived bad write
> lkdtm: do_nothing was hijacked!
>
> Looks like a standard compiler barrier(); is not enough to force
> GCC to use the modified function descriptor. Add to add a fake empty
> inline assembly to force GCC to reload the function descriptor.
>
> Signed-off-by: Christophe Leroy <[email protected]>
> ---
> drivers/misc/lkdtm/core.c | 1 +
> drivers/misc/lkdtm/lkdtm.h | 1 +
> drivers/misc/lkdtm/perms.c | 22 ++++++++++++++++++++++
> 3 files changed, 24 insertions(+)
>
> diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
> index fe6fd34b8caf..de092aa03b5d 100644
> --- a/drivers/misc/lkdtm/core.c
> +++ b/drivers/misc/lkdtm/core.c
> @@ -148,6 +148,7 @@ static const struct crashtype crashtypes[] = {
> CRASHTYPE(WRITE_RO),
> CRASHTYPE(WRITE_RO_AFTER_INIT),
> CRASHTYPE(WRITE_KERN),
> + CRASHTYPE(WRITE_OPD),
> CRASHTYPE(REFCOUNT_INC_OVERFLOW),
> CRASHTYPE(REFCOUNT_ADD_OVERFLOW),
> CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW),
> diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
> index c212a253edde..188bd0fd6575 100644
> --- a/drivers/misc/lkdtm/lkdtm.h
> +++ b/drivers/misc/lkdtm/lkdtm.h
> @@ -105,6 +105,7 @@ void __init lkdtm_perms_init(void);
> void lkdtm_WRITE_RO(void);
> void lkdtm_WRITE_RO_AFTER_INIT(void);
> void lkdtm_WRITE_KERN(void);
> +void lkdtm_WRITE_OPD(void);
> void lkdtm_EXEC_DATA(void);
> void lkdtm_EXEC_STACK(void);
> void lkdtm_EXEC_KMALLOC(void);
> diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
> index 96b3ebfcb8ed..3870bc82d40d 100644
> --- a/drivers/misc/lkdtm/perms.c
> +++ b/drivers/misc/lkdtm/perms.c
> @@ -44,6 +44,11 @@ static noinline void do_overwritten(void)
> 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)
> {
> memcpy(fdesc, do_nothing, sizeof(*fdesc));
> @@ -143,6 +148,23 @@ void lkdtm_WRITE_KERN(void)
> 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("Platform doesn't have function descriptors.\n");

This should be more explicit ('xfail'):

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));

Since this is a descriptor, I assume no icache flush is needed. Are
function descriptors strictly dcache? (Is anything besides just a
barrier needed?)

> + func();
> +}
> +
> void lkdtm_EXEC_DATA(void)
> {
> execute_location(data_area, CODE_WRITE);
> --
> 2.31.1
>

--
Kees Cook

2021-10-18 03:28:23

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 13/13] lkdtm: Add a test for function descriptors protection



Le 15/10/2021 à 23:35, Kees Cook a écrit :
> On Thu, Oct 14, 2021 at 07:50:02AM +0200, Christophe Leroy wrote:
>> Add WRITE_OPD to check that you can't modify function
>> descriptors.
>>
>> Gives the following result when function descriptors are
>> not protected:
>>
>> lkdtm: Performing direct entry WRITE_OPD
>> lkdtm: attempting bad 16 bytes write at c00000000269b358
>> lkdtm: FAIL: survived bad write
>> lkdtm: do_nothing was hijacked!
>>
>> Looks like a standard compiler barrier(); is not enough to force
>> GCC to use the modified function descriptor. Add to add a fake empty
>> inline assembly to force GCC to reload the function descriptor.
>>
>> Signed-off-by: Christophe Leroy <[email protected]>
>> ---
>> drivers/misc/lkdtm/core.c | 1 +
>> drivers/misc/lkdtm/lkdtm.h | 1 +
>> drivers/misc/lkdtm/perms.c | 22 ++++++++++++++++++++++
>> 3 files changed, 24 insertions(+)
>>
>> diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
>> index fe6fd34b8caf..de092aa03b5d 100644
>> --- a/drivers/misc/lkdtm/core.c
>> +++ b/drivers/misc/lkdtm/core.c
>> @@ -148,6 +148,7 @@ static const struct crashtype crashtypes[] = {
>> CRASHTYPE(WRITE_RO),
>> CRASHTYPE(WRITE_RO_AFTER_INIT),
>> CRASHTYPE(WRITE_KERN),
>> + CRASHTYPE(WRITE_OPD),
>> CRASHTYPE(REFCOUNT_INC_OVERFLOW),
>> CRASHTYPE(REFCOUNT_ADD_OVERFLOW),
>> CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW),
>> diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
>> index c212a253edde..188bd0fd6575 100644
>> --- a/drivers/misc/lkdtm/lkdtm.h
>> +++ b/drivers/misc/lkdtm/lkdtm.h
>> @@ -105,6 +105,7 @@ void __init lkdtm_perms_init(void);
>> void lkdtm_WRITE_RO(void);
>> void lkdtm_WRITE_RO_AFTER_INIT(void);
>> void lkdtm_WRITE_KERN(void);
>> +void lkdtm_WRITE_OPD(void);
>> void lkdtm_EXEC_DATA(void);
>> void lkdtm_EXEC_STACK(void);
>> void lkdtm_EXEC_KMALLOC(void);
>> diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
>> index 96b3ebfcb8ed..3870bc82d40d 100644
>> --- a/drivers/misc/lkdtm/perms.c
>> +++ b/drivers/misc/lkdtm/perms.c
>> @@ -44,6 +44,11 @@ static noinline void do_overwritten(void)
>> 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)
>> {
>> memcpy(fdesc, do_nothing, sizeof(*fdesc));
>> @@ -143,6 +148,23 @@ void lkdtm_WRITE_KERN(void)
>> 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("Platform doesn't have function descriptors.\n");
>
> This should be more explicit ('xfail'):
>
> pr_info("XFAIL: platform doesn't use function descriptors.\n");

Ok


>
>> + 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));
>
> Since this is a descriptor, I assume no icache flush is needed. Are
> function descriptors strictly dcache? (Is anything besides just a
> barrier needed?)

No flush is needed, the code just loads the function address from memory
into CTR, loads R2 and branch to CTR:

19c: e9 21 00 70 ld r9,112(r1)
1a0: e9 49 00 00 ld r10,0(r9)
1a4: 7d 49 03 a6 mtctr r10
1a8: e8 49 00 08 ld r2,8(r9)
1ac: 4e 80 04 21 bctrl


>
>> + func();
>> +}
>> +
>> void lkdtm_EXEC_DATA(void)
>> {
>> execute_location(data_area, CODE_WRITE);
>> --
>> 2.31.1
>>
>

2021-10-18 03:30:03

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 12/13] lkdtm: Fix execute_[user]_location()



Le 15/10/2021 à 23:31, Kees Cook a écrit :
> On Thu, Oct 14, 2021 at 07:50:01AM +0200, Christophe Leroy wrote:
>> execute_location() and execute_user_location() intent
>> to copy do_nothing() text and execute it at a new location.
>> However, at the time being it doesn't copy do_nothing() function
>> but do_nothing() function descriptor which still points to the
>> original text. So at the end it still executes do_nothing() at
>> its original location allthough using a copied function descriptor.
>>
>> So, fix that by really copying do_nothing() text and build a new
>> function descriptor by copying do_nothing() function descriptor and
>> updating the target address with the new location.
>>
>> Also fix the displayed addresses by dereferencing do_nothing()
>> function descriptor.
>>
>> Signed-off-by: Christophe Leroy <[email protected]>
>> ---
>> drivers/misc/lkdtm/perms.c | 25 +++++++++++++++++++++----
>> include/asm-generic/sections.h | 5 +++++
>> 2 files changed, 26 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
>> index 5266dc28df6e..96b3ebfcb8ed 100644
>> --- a/drivers/misc/lkdtm/perms.c
>> +++ b/drivers/misc/lkdtm/perms.c
>> @@ -44,19 +44,32 @@ static noinline void do_overwritten(void)
>> return;
>> }
>>
>> +static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
>> +{
>> + memcpy(fdesc, do_nothing, sizeof(*fdesc));
>> + fdesc->addr = (unsigned long)dst;
>> + barrier();
>> +
>> + return fdesc;
>> +}
>
> How about collapsing the "have_function_descriptors()" check into
> setup_function_descriptor()?
>
> static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
> {
> if (__is_defined(HAVE_FUNCTION_DESCRIPTORS)) {
> memcpy(fdesc, do_nothing, sizeof(*fdesc));
> fdesc->addr = (unsigned long)dst;
> barrier();
> return fdesc;
> } else {
> return dst;
> }
> }

Ok

>
>> +
>> static noinline void execute_location(void *dst, bool write)
>> {
>> void (*func)(void) = dst;
>> + 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();
>>
>> if (write == CODE_WRITE) {
>> - memcpy(dst, do_nothing, EXEC_SIZE);
>> + memcpy(dst, do_nothing_text, EXEC_SIZE);
>> flush_icache_range((unsigned long)dst,
>> (unsigned long)dst + EXEC_SIZE);
>> }
>> pr_info("attempting bad execution at %px\n", func);
>> + if (have_function_descriptors())
>> + func = setup_function_descriptor(&fdesc, dst);
>> func();
>> pr_err("FAIL: func returned\n");
>> }
>> @@ -67,15 +80,19 @@ static void execute_user_location(void *dst)
>>
>> /* Intentionally crossing kernel/user memory boundary. */
>> void (*func)(void) = dst;
>> + 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();
>>
>> - 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);
>> if (copied < EXEC_SIZE)
>> return;
>> pr_info("attempting bad execution at %px\n", func);
>> + if (have_function_descriptors())
>> + func = setup_function_descriptor(&fdesc, dst);
>> func();
>> pr_err("FAIL: func returned\n");
>> }
>
>
>> diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
>> index 76163883c6ff..d225318538bd 100644
>> --- a/include/asm-generic/sections.h
>> +++ b/include/asm-generic/sections.h
>> @@ -70,6 +70,11 @@ typedef struct {
>> } func_desc_t;
>> #endif
>>
>> +static inline bool have_function_descriptors(void)
>> +{
>> + return __is_defined(HAVE_FUNCTION_DESCRIPTORS);
>> +}
>> +
>> /* random extra sections (if any). Override
>> * in asm/sections.h */
>> #ifndef arch_is_kernel_text
>
> This hunk seems like it should live in a separate patch.
>

Ok I move it in a previous patch.

2021-11-16 15:08:59

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v2 12/13] lkdtm: Fix execute_[user]_location()

Hi Kees,

Le 16/10/2021 à 08:42, Christophe Leroy a écrit :
>
>
> Le 15/10/2021 à 23:31, Kees Cook a écrit :
>> On Thu, Oct 14, 2021 at 07:50:01AM +0200, Christophe Leroy wrote:
>>> execute_location() and execute_user_location() intent
>>> to copy do_nothing() text and execute it at a new location.
>>> However, at the time being it doesn't copy do_nothing() function
>>> but do_nothing() function descriptor which still points to the
>>> original text. So at the end it still executes do_nothing() at
>>> its original location allthough using a copied function descriptor.
>>>
>>> So, fix that by really copying do_nothing() text and build a new
>>> function descriptor by copying do_nothing() function descriptor and
>>> updating the target address with the new location.
>>>
>>> Also fix the displayed addresses by dereferencing do_nothing()
>>> function descriptor.
>>>
>>> Signed-off-by: Christophe Leroy <[email protected]>
>>> ---
>>>   drivers/misc/lkdtm/perms.c     | 25 +++++++++++++++++++++----
>>>   include/asm-generic/sections.h |  5 +++++
>>>   2 files changed, 26 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c
>>> index 5266dc28df6e..96b3ebfcb8ed 100644
>>> --- a/drivers/misc/lkdtm/perms.c
>>> +++ b/drivers/misc/lkdtm/perms.c
>>> @@ -44,19 +44,32 @@ static noinline void do_overwritten(void)
>>>       return;
>>>   }
>>> +static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
>>> +{
>>> +    memcpy(fdesc, do_nothing, sizeof(*fdesc));
>>> +    fdesc->addr = (unsigned long)dst;
>>> +    barrier();
>>> +
>>> +    return fdesc;
>>> +}
>>
>> How about collapsing the "have_function_descriptors()" check into
>> setup_function_descriptor()?
>>
>> static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
>> {
>>     if (__is_defined(HAVE_FUNCTION_DESCRIPTORS)) {
>>         memcpy(fdesc, do_nothing, sizeof(*fdesc));
>>         fdesc->addr = (unsigned long)dst;
>>         barrier();
>>         return fdesc;
>>     } else {
>>         return dst;
>>     }
>> }
>
> Ok
>

...

>>
>>> diff --git a/include/asm-generic/sections.h
>>> b/include/asm-generic/sections.h
>>> index 76163883c6ff..d225318538bd 100644
>>> --- a/include/asm-generic/sections.h
>>> +++ b/include/asm-generic/sections.h
>>> @@ -70,6 +70,11 @@ typedef struct {
>>>   } func_desc_t;
>>>   #endif
>>> +static inline bool have_function_descriptors(void)
>>> +{
>>> +    return __is_defined(HAVE_FUNCTION_DESCRIPTORS);
>>> +}
>>> +
>>>   /* random extra sections (if any).  Override
>>>    * in asm/sections.h */
>>>   #ifndef arch_is_kernel_text
>>
>> This hunk seems like it should live in a separate patch.
>>
>
> Ok I move it in a previous patch.


Do you have any additional feedback or comment on series v3 ?

What's the way forward, should it go via LKDTM tree or via powerpc tree
or another tree ? I see there are neither Ack-by nor Reviewed-by for the
last 2 patches.

Thanks
Christophe