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 6 is not absolutely necessary but it is a good trivial cleanup.
Christophe Leroy (10):
powerpc: Move 'struct ppc64_opd_entry' back into asm/elf.h
powerpc: Rename 'funcaddr' to 'addr' in 'struct ppc64_opd_entry'
ia64: Rename 'ip' to 'addr' in 'struct fdesc'
asm-generic: Use HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR to define
associated stubs
asm-generic: Define 'funct_descr_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()
arch/ia64/include/asm/elf.h | 2 +-
arch/ia64/include/asm/sections.h | 24 ++---------
arch/ia64/kernel/module.c | 6 +--
arch/parisc/include/asm/sections.h | 16 +++----
arch/parisc/kernel/process.c | 21 ---------
arch/powerpc/include/asm/elf.h | 7 +++
arch/powerpc/include/asm/sections.h | 30 +++----------
arch/powerpc/include/uapi/asm/elf.h | 8 ----
arch/powerpc/kernel/module_64.c | 6 +--
drivers/misc/lkdtm/perms.c | 66 +++++++++++++++++++++++------
include/asm-generic/sections.h | 24 ++++++++++-
11 files changed, 102 insertions(+), 108 deletions(-)
--
2.31.1
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
@@ -153,7 +153,14 @@ void lkdtm_EXEC_VMALLOC(void)voidlkdtm_EXEC_RODATA(void){-execute_location(lkdtm_rodata_do_nothing,CODE_AS_IS);+pr_info("attempting ok execution at %px\n",+dereference_symbol_descriptor(do_nothing));+do_nothing();++pr_info("attempting bad execution at %px\n",+dereference_symbol_descriptor(lkdtm_rodata_do_nothing));+lkdtm_rodata_do_nothing();+pr_err("FAIL: func returned\n");}voidlkdtm_EXEC_USERSPACE(void)
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 <redacted>
---
drivers/misc/lkdtm/perms.c | 45 +++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
@@ -44,19 +44,42 @@ static noinline void do_overwritten(void)return;}+staticvoid*setup_function_descriptor(funct_descr_t*fdesc,void*dst)+{+interr;++if(!__is_defined(HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR))+returndst;++err=copy_from_kernel_nofault(fdesc,do_nothing,sizeof(*fdesc));+if(err<0)+returnERR_PTR(err);++fdesc->addr=(unsignedlong)dst;+barrier();++returnfdesc;+}+staticnoinlinevoidexecute_location(void*dst,boolwrite){-void(*func)(void)=dst;+void(*func)(void);+funct_descr_tfdesc;+void*do_nothing_text=dereference_symbol_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((unsignedlong)dst,(unsignedlong)dst+EXEC_SIZE);}-pr_info("attempting bad execution at %px\n",func);+func=setup_function_descriptor(&fdesc,dst);+if(IS_ERR(func))+return;++pr_info("attempting bad execution at %px\n",dst);func();pr_err("FAIL: func returned\n");}
@@ -66,16 +89,22 @@ static void execute_user_location(void *dst)intcopied;/* Intentionally crossing kernel/user memory boundary. */-void(*func)(void)=dst;+void(*func)(void);+funct_descr_tfdesc;+void*do_nothing_text=dereference_symbol_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,(unsignedlong)dst,do_nothing,+copied=access_process_vm(current,(unsignedlong)dst,do_nothing_text,EXEC_SIZE,FOLL_WRITE);if(copied<EXEC_SIZE)return;-pr_info("attempting bad execution at %px\n",func);+func=setup_function_descriptor(&fdesc,dst);+if(IS_ERR(func))+return;++pr_info("attempting bad execution at %px\n",dst);func();pr_err("FAIL: func returned\n");}
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.
Signed-off-by: Christophe Leroy <redacted>
---
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(-)
@@ -179,7 +179,7 @@ void relocate(unsigned long final_address);/* There's actually a third entry here, but it's unused */structppc64_opd_entry{-unsignedlongfuncaddr;+unsignedlongaddr;unsignedlongr2;};
@@ -72,11 +72,11 @@ static func_desc_t func_desc(unsigned long addr)}staticunsignedlongfunc_addr(unsignedlongaddr){-returnfunc_desc(addr).funcaddr;+returnfunc_desc(addr).addr;}staticunsignedlongstub_func_addr(func_desc_tfunc){-returnfunc.funcaddr;+returnfunc.addr;}staticunsignedintlocal_entry_offset(constElf64_Sym*sym){
@@ -187,7 +187,7 @@ static int relacmp(const void *_x, const void *_y)staticunsignedlongget_stubs_size(constElf64_Ehdr*hdr,constElf64_Shdr*sechdrs){-/* One extra reloc so it's always 0-funcaddr terminated */+/* One extra reloc so it's always 0-addr terminated */unsignedlongrelocs=1;unsignedi;
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 fdesc' accordingly.
Signed-off-by: Christophe Leroy <redacted>
---
arch/ia64/include/asm/elf.h | 2 +-
arch/ia64/include/asm/sections.h | 2 +-
arch/ia64/kernel/module.c | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
@@ -602,15 +602,15 @@ get_fdesc (struct module *mod, uint64_t value, int *okp)returnvalue;/* Look for existing function descriptor. */-while(fdesc->ip){-if(fdesc->ip==value)+while(fdesc->addr){+if(fdesc->addr==value)return(uint64_t)fdesc;if((uint64_t)++fdesc>=mod->arch.opd->sh_addr+mod->arch.opd->sh_size)BUG();}/* Create new one */-fdesc->ip=value;+fdesc->addr=value;fdesc->gp=mod->arch.gp;return(uint64_t)fdesc;}
LKDTM tests display that the run do_nothing() at a given
address, but in reality do_nothing() is inlined into the
caller.
Force it out of line so that it really runs text at the
displayed address.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -21,7 +21,7 @@/* This is non-const, so it will end up in the .data section. */staticu8data_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. */staticconstunsignedlongrodata=0xAA55AA55;/* This is marked __ro_after_init, so it should ultimately be .rodata. */
WRITE_KERN is supposed to overwrite some kernel text, namely
do_overwritten() function.
But at the time being it overwrites do_overwritten() function
descriptor, not function text.
Fix it by dereferencing the function descriptor to obtain
function text pointer.
And make do_overwritten() noinline so that it is really
do_overwritten() which is called by lkdtm_WRITE_KERN().
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
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.
Signed-off-by: Christophe Leroy <redacted>
---
arch/ia64/include/asm/sections.h | 1 +
arch/parisc/include/asm/sections.h | 1 +
arch/powerpc/include/asm/sections.h | 1 +
include/asm-generic/sections.h | 3 +++
4 files changed, 6 insertions(+)
@@ -4,6 +4,7 @@#ifdef CONFIG_64BIT#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+typedefElf64_Fdescfunct_descr_t;#endif/* nothing to see, move along */
'struct ppc64_opd_entry' doesn't belong to uapi/asm/elf.h
It was initially in module_64.c and commit 2d291e902791 ("Fix compile
failure with non modular builds") moved it into asm/elf.h
But it was by mistake added outside of __KERNEL__ section,
therefore commit c3617f72036c ("UAPI: (Scripted) Disintegrate
arch/powerpc/include/asm") moved it to uapi/asm/elf.h
Move it back into asm/elf.h, this brings it back in line with
IA64 and PARISC architectures.
Fixes: 2d291e902791 ("Fix compile failure with non modular builds")
Signed-off-by: Christophe Leroy <redacted>
---
arch/powerpc/include/asm/elf.h | 7 +++++++
arch/powerpc/include/uapi/asm/elf.h | 8 --------
2 files changed, 7 insertions(+), 8 deletions(-)
@@ -176,4 +176,11 @@ do { \/* Relocate the kernel image to @final_address */voidrelocate(unsignedlongfinal_address);+/* There's actually a third entry here, but it's unused */+structppc64_opd_entry+{+unsignedlongfuncaddr;+unsignedlongr2;+};+#endif /* _ASM_POWERPC_ELF_H */
@@ -289,12 +289,4 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];/* Keep this the last entry. */#define R_PPC64_NUM 253-/* There's actually a third entry here, but it's unused */-structppc64_opd_entry-{-unsignedlongfuncaddr;-unsignedlongr2;-};--#endif /* _UAPI_ASM_POWERPC_ELF_H */
@@ -2,6 +2,10 @@#ifndef _PARISC_SECTIONS_H#define _PARISC_SECTIONS_H+#ifdef CONFIG_64BIT+#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+#endif+/* nothing to see, move along */#include<asm-generic/sections.h>
@@ -2,6 +2,10 @@#ifndef _PARISC_SECTIONS_H#define _PARISC_SECTIONS_H+#ifdef CONFIG_64BIT+#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+#endif+/* nothing to see, move along */#include<asm-generic/sections.h>
@@ -2,6 +2,10 @@#ifndef _PARISC_SECTIONS_H#define _PARISC_SECTIONS_H+#ifdef CONFIG_64BIT+#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+#endif+/* nothing to see, move along */#include<asm-generic/sections.h>
@@ -2,6 +2,10 @@#ifndef _PARISC_SECTIONS_H#define _PARISC_SECTIONS_H+#ifdef CONFIG_64BIT+#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+#endif+/* nothing to see, move along */#include<asm-generic/sections.h>
@@ -59,7 +59,8 @@ extern char __noinstr_text_start[], __noinstr_text_end[];extern__visibleconstvoid__nosave_begin,__nosave_end;/* Function descriptor handling (if any). Override in asm/sections.h */-#ifndef dereference_function_descriptor+#ifdef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR+#else
why not
#ifndef HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR
instead of #if/#else ?
To avoid changing it again in patch 6, or getting an ugly #ifndef/#else at
the end.
Ok.
Building on parisc fails at multiple files like this:
CC mm/filemap.o
In file included from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/interrupt.h:21,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/trace_recursion.h:5,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/ftrace.h:10,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/perf_event.h:49,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/trace_events.h:10,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/trace/syscall.h:7,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/include/linux/syscalls.h:87,
from /home/cvs/parisc/git-kernel/linus-linux-2.6/init/initramfs.c:11:
/home/cvs/parisc/git-kernel/linus-linux-2.6/arch/parisc/include/asm/sections.h:7:9: error: unknown type name ‘Elf64_Fdesc’
7 | typedef Elf64_Fdesc funct_descr_t;
| ^~~~~~~~~~~
So, you still need e.g. this patch:
In general to save space I think it would be beneficial if the
dereference_kernel_function_descriptor() and
dereference_kernel_function_descriptor() functions would go as real
functions a c-file instead of just being inlined functions in the
asm-generic/sections.h header file.
Other than that it's a nice cleanup!
Helge
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2021-10-12 07:10:42
Christophe Leroy [off-list ref] writes:
'struct ppc64_opd_entry' doesn't belong to uapi/asm/elf.h
Agree, but ...
It was initially in module_64.c and commit 2d291e902791 ("Fix compile
failure with non modular builds") moved it into asm/elf.h
But it was by mistake added outside of __KERNEL__ section,
therefore commit c3617f72036c ("UAPI: (Scripted) Disintegrate
arch/powerpc/include/asm") moved it to uapi/asm/elf.h
... it's been visible to userspace since the first commit moved it, ~13
years ago in 2008, v2.6.27.
Move it back into asm/elf.h, this brings it back in line with
IA64 and PARISC architectures.
Removing it from the uapi header risks breaking userspace, I doubt
anything uses it, but who knows.
Given how long it's been there I think it's a bit risky to remove it :/
@@ -176,4 +176,11 @@ do { \/* Relocate the kernel image to @final_address */voidrelocate(unsignedlongfinal_address);+/* There's actually a third entry here, but it's unused */+structppc64_opd_entry+{+unsignedlongfuncaddr;+unsignedlongr2;+};+#endif /* _ASM_POWERPC_ELF_H */
@@ -289,12 +289,4 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];/* Keep this the last entry. */#define R_PPC64_NUM 253-/* There's actually a third entry here, but it's unused */-structppc64_opd_entry-{-unsignedlongfuncaddr;-unsignedlongr2;-};
Rather than removing it we can make it uapi only with:
#ifndef __KERNEL__
/* There's actually a third entry here, but it's unused */
struct ppc64_opd_entry
{
unsigned long funcaddr;
unsigned long r2;
};
#endif
And then we can do whatever we want with the kernel internal version.
cheers
On Tue, Oct 12, 2021 at 9:10 AM Michael Ellerman [off-list ref] wrote:
Christophe Leroy [off-list ref] writes:
quoted
'struct ppc64_opd_entry' doesn't belong to uapi/asm/elf.h
But it was by mistake added outside of __KERNEL__ section,
therefore commit c3617f72036c ("UAPI: (Scripted) Disintegrate
arch/powerpc/include/asm") moved it to uapi/asm/elf.h
... it's been visible to userspace since the first commit moved it, ~13
years ago in 2008, v2.6.27.
quoted
Move it back into asm/elf.h, this brings it back in line with
IA64 and PARISC architectures.
Removing it from the uapi header risks breaking userspace, I doubt
anything uses it, but who knows.
Given how long it's been there I think it's a bit risky to remove it :/
I would not be too worried about it. While we should absolutely
never break existing binaries, changing the visibility of internal
structures in header files only breaks compiling applications
that do rely on these entries, and they really should not be using
this in the first place.
Arnd
On Mon, Oct 11, 2021 at 05:25:28PM +0200, Christophe Leroy wrote:
'struct ppc64_opd_entry' doesn't belong to uapi/asm/elf.h
It was initially in module_64.c and commit 2d291e902791 ("Fix compile
failure with non modular builds") moved it into asm/elf.h
But it was by mistake added outside of __KERNEL__ section,
therefore commit c3617f72036c ("UAPI: (Scripted) Disintegrate
arch/powerpc/include/asm") moved it to uapi/asm/elf.h
Move it back into asm/elf.h, this brings it back in line with
IA64 and PARISC architectures.
Fixes: 2d291e902791 ("Fix compile failure with non modular builds")
Signed-off-by: Christophe Leroy <redacted>
I'd agree with Arnd: this is a reasonable cleanup and nothing should be
using it.
Reviewed-by: Kees Cook <redacted>
--
Kees Cook
On Mon, Oct 11, 2021 at 05:25:29PM +0200, Christophe Leroy wrote:
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.
Signed-off-by: Christophe Leroy <redacted>
On Mon, Oct 11, 2021 at 05:25:30PM +0200, Christophe Leroy wrote:
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 fdesc' accordingly.
Signed-off-by: Christophe Leroy <redacted>
On Mon, Oct 11, 2021 at 05:25:31PM +0200, Christophe Leroy wrote:
Use HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR instead of 'dereference_function_descriptor'
to know whether arch has function descriptors.
Signed-off-by: Christophe Leroy <redacted>
I'd mention the intentionally-empty #if/#else in the commit log, as I,
like Helge, mentally tripped over it in the review. :)
Reviewed-by: Kees Cook <redacted>
--
Kees Cook
On Mon, Oct 11, 2021 at 05:25:32PM +0200, Christophe Leroy 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,
nit: funct_descr_t reads weird to me. why not func_desc_t ? Either way:
Reviewed-by: Kees Cook <redacted>
@@ -4,6 +4,7 @@#ifdef CONFIG_64BIT#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+typedefElf64_Fdescfunct_descr_t;#endif/* nothing to see, move along */
On Mon, Oct 11, 2021 at 05:25:34PM +0200, Christophe Leroy wrote:
LKDTM tests display that the run do_nothing() at a given
address, but in reality do_nothing() is inlined into the
caller.
Force it out of line so that it really runs text at the
displayed address.
Signed-off-by: Christophe Leroy <redacted>
On Mon, Oct 11, 2021 at 05:25:35PM +0200, Christophe Leroy wrote:
quoted hunk
WRITE_KERN is supposed to overwrite some kernel text, namely
do_overwritten() function.
But at the time being it overwrites do_overwritten() function
descriptor, not function text.
Fix it by dereferencing the function descriptor to obtain
function text pointer.
And make do_overwritten() noinline so that it is really
do_overwritten() which is called by lkdtm_WRITE_KERN().
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
@@ -153,7 +153,14 @@ void lkdtm_EXEC_VMALLOC(void)voidlkdtm_EXEC_RODATA(void){-execute_location(lkdtm_rodata_do_nothing,CODE_AS_IS);+pr_info("attempting ok execution at %px\n",+dereference_symbol_descriptor(do_nothing));+do_nothing();++pr_info("attempting bad execution at %px\n",+dereference_symbol_descriptor(lkdtm_rodata_do_nothing));+lkdtm_rodata_do_nothing();+pr_err("FAIL: func returned\n");}voidlkdtm_EXEC_USERSPACE(void)
On Mon, Oct 11, 2021 at 05:25:37PM +0200, Christophe Leroy wrote:
quoted hunk
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 <redacted>
---
drivers/misc/lkdtm/perms.c | 45 +++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
@@ -44,19 +44,42 @@ static noinline void do_overwritten(void)return;}+staticvoid*setup_function_descriptor(funct_descr_t*fdesc,void*dst)+{+interr;++if(!__is_defined(HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR))+returndst;++err=copy_from_kernel_nofault(fdesc,do_nothing,sizeof(*fdesc));+if(err<0)+returnERR_PTR(err);++fdesc->addr=(unsignedlong)dst;+barrier();++returnfdesc;+}+staticnoinlinevoidexecute_location(void*dst,boolwrite){-void(*func)(void)=dst;+void(*func)(void);+funct_descr_tfdesc;+void*do_nothing_text=dereference_symbol_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((unsignedlong)dst,(unsignedlong)dst+EXEC_SIZE);}-pr_info("attempting bad execution at %px\n",func);+func=setup_function_descriptor(&fdesc,dst);+if(IS_ERR(func))+return;
I think this error case should complain with some details. :) Maybe:
pr_err("FAIL: could not build function descriptor for %px\n", dst);
+
+ pr_info("attempting bad execution at %px\n", dst);
And then leave this pr_info() as it was, before the
setup_function_descriptor() call.
quoted hunk
func();
pr_err("FAIL: func returned\n");
}
@@ -66,16 +89,22 @@ static void execute_user_location(void *dst) int copied; /* Intentionally crossing kernel/user memory boundary. */- void (*func)(void) = dst;+ void (*func)(void);+ funct_descr_t fdesc;+ void *do_nothing_text = dereference_symbol_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);+ func = setup_function_descriptor(&fdesc, dst);+ if (IS_ERR(func))+ return;++ pr_info("attempting bad execution at %px\n", dst);
On Mon, Oct 11, 2021 at 05:25:36PM +0200, Christophe Leroy wrote:
quoted hunk
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
@@ -153,7 +153,14 @@ void lkdtm_EXEC_VMALLOC(void)voidlkdtm_EXEC_RODATA(void){-execute_location(lkdtm_rodata_do_nothing,CODE_AS_IS);+pr_info("attempting ok execution at %px\n",+dereference_symbol_descriptor(do_nothing));+do_nothing();++pr_info("attempting bad execution at %px\n",+dereference_symbol_descriptor(lkdtm_rodata_do_nothing));+lkdtm_rodata_do_nothing();+pr_err("FAIL: func returned\n");}
(In re-reading this more carefully, I see now why kallsyms.h is used
earlier: _function_ vs _symbol_ descriptor.)
In the next patch:
static noinline void execute_location(void *dst, bool write)
{
...
func = setup_function_descriptor(&fdesc, dst);
if (IS_ERR(func))
return;
pr_info("attempting bad execution at %px\n", dst);
func();
pr_err("FAIL: func returned\n");
}
What are the conditions for which dereference_symbol_descriptor works
but dereference _function_descriptor doesn't?
--
Kees Cook
On Mon, Oct 11, 2021 at 05:25:32PM +0200, Christophe Leroy wrote:
quoted
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,
nit: funct_descr_t reads weird to me. why not func_desc_t ? Either way:
func_desc_t already exists in powerpc. I have a patch to remove it as it is redundant with struct ppc64_opd_entry, but I didnt' want to include it in this series.
But after all I can add it in this series, I'll add it in v2.
Christophe
@@ -4,6 +4,7 @@#ifdef CONFIG_64BIT#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1+typedefElf64_Fdescfunct_descr_t;#endif/* nothing to see, move along */
On Wed, Oct 13, 2021 at 09:23:56AM +0200, Christophe Leroy wrote:
Le 13/10/2021 à 09:01, Kees Cook a écrit :
quoted
On Mon, Oct 11, 2021 at 05:25:32PM +0200, Christophe Leroy wrote:
quoted
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,
nit: funct_descr_t reads weird to me. why not func_desc_t ? Either way:
func_desc_t already exists in powerpc. I have a patch to remove it as it is
redundant with struct ppc64_opd_entry, but I didnt' want to include it in
this series.
But after all I can add it in this series, I'll add it in v2.
Ah-ha! That works for me. :) Thanks!
-Kees
--
Kees Cook
On Mon, Oct 11, 2021 at 05:25:35PM +0200, Christophe Leroy wrote:
quoted
WRITE_KERN is supposed to overwrite some kernel text, namely
do_overwritten() function.
But at the time being it overwrites do_overwritten() function
descriptor, not function text.
Fix it by dereferencing the function descriptor to obtain
function text pointer.
And make do_overwritten() noinline so that it is really
do_overwritten() which is called by lkdtm_WRITE_KERN().
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
On Mon, Oct 11, 2021 at 05:25:36PM +0200, Christophe Leroy wrote:
quoted
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
I don't understand this. Why does the next patch not fix this?
Well, probably it would, but it looked incorrect in my mind.
lkdtm_rodata_do_nothing() is a function which has its own function descriptor, it is not a copy of do_nothing().
If we use execute_location() as modified by next patch, then we will execute it using the function descriptor of do_nothing(). Allthough it most likely works (at least on powerpc as it uses the same TOC) it looks odd to me to do so.
Am I missing something ?
Christophe
-Kees
quoted
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
@@ -153,7 +153,14 @@ void lkdtm_EXEC_VMALLOC(void)voidlkdtm_EXEC_RODATA(void){-execute_location(lkdtm_rodata_do_nothing,CODE_AS_IS);+pr_info("attempting ok execution at %px\n",+dereference_symbol_descriptor(do_nothing));+do_nothing();++pr_info("attempting bad execution at %px\n",+dereference_symbol_descriptor(lkdtm_rodata_do_nothing));+lkdtm_rodata_do_nothing();+pr_err("FAIL: func returned\n");}voidlkdtm_EXEC_USERSPACE(void)
On Mon, Oct 11, 2021 at 05:25:36PM +0200, Christophe Leroy wrote:
quoted
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
@@ -153,7 +153,14 @@ void lkdtm_EXEC_VMALLOC(void)voidlkdtm_EXEC_RODATA(void){-execute_location(lkdtm_rodata_do_nothing,CODE_AS_IS);+pr_info("attempting ok execution at %px\n",+dereference_symbol_descriptor(do_nothing));+do_nothing();++pr_info("attempting bad execution at %px\n",+dereference_symbol_descriptor(lkdtm_rodata_do_nothing));+lkdtm_rodata_do_nothing();+pr_err("FAIL: func returned\n");}
(In re-reading this more carefully, I see now why kallsyms.h is used
earlier: _function_ vs _symbol_ descriptor.)
In the next patch:
static noinline void execute_location(void *dst, bool write)
{
...
func = setup_function_descriptor(&fdesc, dst);
if (IS_ERR(func))
return;
pr_info("attempting bad execution at %px\n", dst);
func();
pr_err("FAIL: func returned\n");
}
What are the conditions for which dereference_symbol_descriptor works
but dereference _function_descriptor doesn't?
When LKDTM is built as a module I guess ?
Christophe
On Mon, Oct 11, 2021 at 05:25:36PM +0200, Christophe Leroy wrote:
quoted
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
void lkdtm_EXEC_RODATA(void)
{
- execute_location(lkdtm_rodata_do_nothing, CODE_AS_IS);
+ pr_info("attempting ok execution at %px\n",
+ dereference_symbol_descriptor(do_nothing));
+ do_nothing();
+
+ pr_info("attempting bad execution at %px\n",
+ dereference_symbol_descriptor(lkdtm_rodata_do_nothing));
+ lkdtm_rodata_do_nothing();
+ pr_err("FAIL: func returned\n");
}
(In re-reading this more carefully, I see now why kallsyms.h is used
earlier: _function_ vs _symbol_ descriptor.)
In the next patch:
static noinline void execute_location(void *dst, bool write)
{
...
func = setup_function_descriptor(&fdesc, dst);
if (IS_ERR(func))
return;
pr_info("attempting bad execution at %px\n", dst);
func();
pr_err("FAIL: func returned\n");
}
What are the conditions for which dereference_symbol_descriptor works
but dereference _function_descriptor doesn't?
When LKDTM is built as a module I guess ?
To be more precise, dereference_symbol_descriptor() calls either dereference_kernel_function_descriptor() or dereference_module_function_descriptor()
Both functions call dereference_function_descriptor() after checking that we want to dereference something that is in the OPD section.
If we call dereference_function_descriptor() directly instead of dereference_symbol_descriptor() we skip the range verification and may dereference something that is not a function descriptor.
Should we do that ?
Christophe
On Mon, Oct 11, 2021 at 05:25:33PM +0200, Christophe Leroy wrote:
quoted
dereference_function_descriptor() and
dereference_kernel_function_descriptor() are identical on the
three architectures implementing them.
Make it common.
Signed-off-by: Christophe Leroy <redacted>
---
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 | 18 ++++++++++++++++++
5 files changed, 18 insertions(+), 72 deletions(-)
A diffstat to love. :)
Reviewed-by: Kees Cook <redacted>
Unless somebody minds, I will make them out of line as
suggested by Helge in he's comment to patch 4.
Allthough there is no spectacular size reduction, the functions
are not worth being inlined as they are not used in critical pathes.
On Wed, Oct 13, 2021 at 1:20 PM Christophe Leroy
[off-list ref] wrote:
Le 13/10/2021 à 09:02, Kees Cook a écrit :
quoted
On Mon, Oct 11, 2021 at 05:25:33PM +0200, Christophe Leroy wrote:
quoted
dereference_function_descriptor() and
dereference_kernel_function_descriptor() are identical on the
three architectures implementing them.
Make it common.
Signed-off-by: Christophe Leroy <redacted>
---
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 | 18 ++++++++++++++++++
5 files changed, 18 insertions(+), 72 deletions(-)
A diffstat to love. :)
Reviewed-by: Kees Cook <redacted>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Unless somebody minds, I will make them out of line as
suggested by Helge in he's comment to patch 4.
Allthough there is no spectacular size reduction, the functions
are not worth being inlined as they are not used in critical pathes.
On Mon, Oct 11, 2021 at 05:25:37PM +0200, Christophe Leroy wrote:
quoted
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 <redacted>
---
drivers/misc/lkdtm/perms.c | 45 +++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
@@ -44,19 +44,42 @@ static noinline void do_overwritten(void)return;}+staticvoid*setup_function_descriptor(funct_descr_t*fdesc,void*dst)+{+interr;++if(!__is_defined(HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR))+returndst;++err=copy_from_kernel_nofault(fdesc,do_nothing,sizeof(*fdesc));+if(err<0)+returnERR_PTR(err);++fdesc->addr=(unsignedlong)dst;+barrier();++returnfdesc;+}+staticnoinlinevoidexecute_location(void*dst,boolwrite){-void(*func)(void)=dst;+void(*func)(void);+funct_descr_tfdesc;+void*do_nothing_text=dereference_symbol_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((unsignedlong)dst,(unsignedlong)dst+EXEC_SIZE);}-pr_info("attempting bad execution at %px\n",func);+func=setup_function_descriptor(&fdesc,dst);+if(IS_ERR(func))+return;
I think this error case should complain with some details. :) Maybe:
pr_err("FAIL: could not build function descriptor for %px\n", dst);
Ok, I was going to add that in v2, but after one more thought I realise that there's no need to use copy_from_kernel_nofault() here, we are copying a static object into our stack, so a memcpy() will be good enough.
quoted
+
+ pr_info("attempting bad execution at %px\n", dst);
And then leave this pr_info() as it was, before the
setup_function_descriptor() call.
quoted
func();
pr_err("FAIL: func returned\n");
}
@@ -66,16 +89,22 @@ static void execute_user_location(void *dst) int copied; /* Intentionally crossing kernel/user memory boundary. */- void (*func)(void) = dst;+ void (*func)(void);+ funct_descr_t fdesc;+ void *do_nothing_text = dereference_symbol_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);+ func = setup_function_descriptor(&fdesc, dst);+ if (IS_ERR(func))+ return;++ pr_info("attempting bad execution at %px\n", dst);
On Mon, Oct 11, 2021 at 05:25:36PM +0200, Christophe Leroy wrote:
quoted
Behind a location, lkdtm_EXEC_RODATA() executes a real function,
not a copy of do_nothing().
So do it directly instead of using execute_location().
And fix displayed addresses by dereferencing the function descriptors.
Signed-off-by: Christophe Leroy <redacted>
---
drivers/misc/lkdtm/perms.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
void lkdtm_EXEC_RODATA(void)
{
- execute_location(lkdtm_rodata_do_nothing, CODE_AS_IS);
+ pr_info("attempting ok execution at %px\n",
+ dereference_symbol_descriptor(do_nothing));
+ do_nothing();
+
+ pr_info("attempting bad execution at %px\n",
+ dereference_symbol_descriptor(lkdtm_rodata_do_nothing));
+ lkdtm_rodata_do_nothing();
+ pr_err("FAIL: func returned\n");
}
(In re-reading this more carefully, I see now why kallsyms.h is used
earlier: _function_ vs _symbol_ descriptor.)
In the next patch:
static noinline void execute_location(void *dst, bool write)
{
...
func = setup_function_descriptor(&fdesc, dst);
if (IS_ERR(func))
return;
pr_info("attempting bad execution at %px\n", dst);
func();
pr_err("FAIL: func returned\n");
}
What are the conditions for which dereference_symbol_descriptor works
but dereference _function_descriptor doesn't?
When LKDTM is built as a module I guess ?
To be more precise, dereference_symbol_descriptor() calls either dereference_kernel_function_descriptor() or dereference_module_function_descriptor()
Both functions call dereference_function_descriptor() after checking that we want to dereference something that is in the OPD section.
If we call dereference_function_descriptor() directly instead of dereference_symbol_descriptor() we skip the range verification and may dereference something that is not a function descriptor.
Should we do that ?
Indeed we are using it only for well known functions so using dereference_function_descriptor() is good enough. I'll use that in v2.
On Mon, Oct 11, 2021 at 05:25:31PM +0200, Christophe Leroy wrote:
quoted
Use HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR instead of 'dereference_function_descriptor'
to know whether arch has function descriptors.
Signed-off-by: Christophe Leroy <redacted>
I'd mention the intentionally-empty #if/#else in the commit log, as I,
like Helge, mentally tripped over it in the review. :)
Reviewed-by: Kees Cook <redacted>
Ok, I did it in v2.
I also renamed it HAVE_FUNCTION_DESCRIPTORS because behind the fact that it has some functions to dereference function descriptor, the main fact is that they have and use function descriptors.
Christophe