Message ID | 20170211013758.3288-1-me@jessfraz.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, 10 Feb 2017, Jess Frazelle wrote: > Marked msi_domain_ops structs as __ro_after_init when called only during init. > Marked syscore_ops structs as __ro_after_init when register_syscore_ops was > called only during init. Most of the caller functions were already annotated as > __init. > unregister_syscore_ops() was never called on these syscore_ops. > This protects the data structure from accidental corruption. Please be more careful with your changelogs. They should not start with telling WHAT you have done. The WHAT we can see from the patch. The interesting information which belongs into the changelog is: WHY and which problem does it solve or which enhancement this is. Let me give you an example: Function pointers are a target for attacks especially when they are located in statically allocated data structures. Some of these data structures are only modified during init and therefor can be made read only after init. struct msi_domain_ops can be made read only after init because they are only updated in the registration case. struct syscore_ops can be made read only after init when they are only registered, but never unregistered. So this would be a proper change log explaning the patch. Emphasis on WOULD, See below. > -static struct syscore_ops irq_gc_syscore_ops = { > +static struct syscore_ops irq_gc_syscore_ops __ro_after_init = { > .suspend = irq_gc_suspend, > .resume = irq_gc_resume, > .shutdown = irq_gc_shutdown, I seriously doubt that syscore_ops can be made __ro_after_init at all. Assume the following: last_init_function() register_syscore_ops(&a_ops) list_add(&a_ops->node, list); apply_ro_after_init() // a_ops are now read only cpuhotplug happens register_syscore_ops(&b_ops) list_add(&b_ops->node, list); ===> Kernel crashes with a write access on RO memory because it tries to link b_ops to a_ops. The same is true for cpuhotunplug operations. > -static struct msi_domain_ops msi_domain_ops_default = { > +static struct msi_domain_ops msi_domain_ops_default __ro_after_init = { This is pointless and just tells me that you did a mechanical search for these ops and then blindly added __ro_after_init instead of analysing how msi_domain_ops_default is used. msi_domain_ops_default are never ever modified, so they should be made 'const' and not __ro_after_init. It's not that hard to figure that out from the code. Thanks, tglx
On Sat, 11 Feb 2017, Thomas Gleixner wrote: > On Fri, 10 Feb 2017, Jess Frazelle wrote: > > > Marked msi_domain_ops structs as __ro_after_init when called only during init. > > Marked syscore_ops structs as __ro_after_init when register_syscore_ops was > > called only during init. Most of the caller functions were already annotated as > > __init. > > unregister_syscore_ops() was never called on these syscore_ops. > > This protects the data structure from accidental corruption. > > Please be more careful with your changelogs. They should not start with > telling WHAT you have done. The WHAT we can see from the patch. > > The interesting information which belongs into the changelog is: WHY and > which problem does it solve or which enhancement this is. Let me give you > an example: > > Function pointers are a target for attacks especially when they are > located in statically allocated data structures. Some of these data > structures are only modified during init and therefor can be made read > only after init. > > struct msi_domain_ops can be made read only after init because they are > only updated in the registration case. > > struct syscore_ops can be made read only after init when they are only > registered, but never unregistered. > > So this would be a proper change log explaning the patch. > > Emphasis on WOULD, See below. > > > -static struct syscore_ops irq_gc_syscore_ops = { > > +static struct syscore_ops irq_gc_syscore_ops __ro_after_init = { > > .suspend = irq_gc_suspend, > > .resume = irq_gc_resume, > > .shutdown = irq_gc_shutdown, > > I seriously doubt that syscore_ops can be made __ro_after_init at all. > > Assume the following: > > last_init_function() > register_syscore_ops(&a_ops) > list_add(&a_ops->node, list); > > apply_ro_after_init() > // a_ops are now read only > > cpuhotplug happens > register_syscore_ops(&b_ops) > list_add(&b_ops->node, list); > > ===> Kernel crashes with a write access on RO memory because it tries > to link b_ops to a_ops. > > The same is true for cpuhotunplug operations. Sorry, cpuhotplug was the wrong example, but loading or unloading the KVM modules will have that effect. See vmx_init()/exit() and kvm_init()/exit() for reference. Thanks, tglx
On February 11, 2017 1:14:52 AM PST, Thomas Gleixner <tglx@linutronix.de> wrote: >On Fri, 10 Feb 2017, Jess Frazelle wrote: > >> Marked msi_domain_ops structs as __ro_after_init when called only >during init. >> Marked syscore_ops structs as __ro_after_init when >register_syscore_ops was >> called only during init. Most of the caller functions were already >annotated as >> __init. >> unregister_syscore_ops() was never called on these syscore_ops. >> This protects the data structure from accidental corruption. > >Please be more careful with your changelogs. They should not start with >telling WHAT you have done. The WHAT we can see from the patch. > >The interesting information which belongs into the changelog is: WHY >and >which problem does it solve or which enhancement this is. Let me give >you >an example: > > Function pointers are a target for attacks especially when they are > located in statically allocated data structures. Some of these data > structures are only modified during init and therefor can be made read > only after init. > >struct msi_domain_ops can be made read only after init because they are > only updated in the registration case. > > struct syscore_ops can be made read only after init when they are only > registered, but never unregistered. > >So this would be a proper change log explaning the patch. Thanks for the clarification. > >Emphasis on WOULD, See below. > >> -static struct syscore_ops irq_gc_syscore_ops = { >> +static struct syscore_ops irq_gc_syscore_ops __ro_after_init = { >> .suspend = irq_gc_suspend, >> .resume = irq_gc_resume, >> .shutdown = irq_gc_shutdown, > >I seriously doubt that syscore_ops can be made __ro_after_init at all. > >Assume the following: > >last_init_function() > register_syscore_ops(&a_ops) > list_add(&a_ops->node, list); > >apply_ro_after_init() > // a_ops are now read only > >cpuhotplug happens > register_syscore_ops(&b_ops) > list_add(&b_ops->node, list); > > ===> Kernel crashes with a write access on RO memory because it tries > to link b_ops to a_ops. > >The same is true for cpuhotunplug operations. This makes sense. Will remove. > >> -static struct msi_domain_ops msi_domain_ops_default = { >> +static struct msi_domain_ops msi_domain_ops_default __ro_after_init >= { > >This is pointless and just tells me that you did a mechanical search >for >these ops and then blindly added __ro_after_init instead of analysing >how >msi_domain_ops_default is used. > >msi_domain_ops_default are never ever modified, so they should be made >'const' and not __ro_after_init. It's not that hard to figure that out >from >the code. Will change to a const. > >Thanks, > > tglx
On Sat, 11 Feb 2017, Jess Frazelle wrote: > On February 11, 2017 1:14:52 AM PST, Thomas Gleixner <tglx@linutronix.de> wrote: > >The same is true for cpuhotunplug operations. > > This makes sense. Will remove. That's true for all other patches touching sysops as well. But instead of giving up I'd recommend to look into the following: Go through all callsites which use un/register_syscore_ops() and figure out how many of them are possibly called post init. From a quick grep I can only find the KVM module, but there might be more. Lets assume it's KVM only. So you could do the following: Put something like this into virt/kvm/kvm_main.c, which is a builtin file static struct syscore_ops ops __ro_after_init = { .... }; int __init foo() { register_ops(&ops); } and because we know that kvm is single instance you can just have: static struct syscore_ops *kvm_ops; void kvm_set_sysop(*vmx_ops) { kvm_ops = ops; } and then have the kvm_syscore callbacks: static callback() { if (kvm_ops) kvm_ops->callback() } Sanity checks and serialization omitted. Then switch kvm_exit/init over to it. After that you can make all syscore_ops __ro_after_init, remove the export from (un)register_syscore_ops() and make that __init. Not much of an effort and probably worth the trouble. Thanks, tglx
Thanks, I'll check them out. On Sat, Feb 11, 2017 at 4:00 AM Thomas Gleixner <tglx@linutronix.de> wrote: > > On Sat, 11 Feb 2017, Jess Frazelle wrote: > > On February 11, 2017 1:14:52 AM PST, Thomas Gleixner <tglx@linutronix.de> wrote: > > >The same is true for cpuhotunplug operations. > > > > This makes sense. Will remove. > > That's true for all other patches touching sysops as well. But instead of > giving up I'd recommend to look into the following: > > Go through all callsites which use un/register_syscore_ops() and figure out > how many of them are possibly called post init. From a quick grep I can > only find the KVM module, but there might be more. > > Lets assume it's KVM only. So you could do the following: > > Put something like this into virt/kvm/kvm_main.c, which is a builtin file > > static struct syscore_ops ops __ro_after_init = { > .... > }; > > int __init foo() > { > register_ops(&ops); > } > > and because we know that kvm is single instance you can just have: > > static struct syscore_ops *kvm_ops; > > void kvm_set_sysop(*vmx_ops) > { > kvm_ops = ops; > } > > and then have the kvm_syscore callbacks: > > static callback() > { > if (kvm_ops) > kvm_ops->callback() > } > > Sanity checks and serialization omitted. Then switch kvm_exit/init over to > it. > > After that you can make all syscore_ops __ro_after_init, remove the export > from (un)register_syscore_ops() and make that __init. > > Not much of an effort and probably worth the trouble. > > Thanks, > > tglx >
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index ee32870079c9..cca63dbaabea 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -623,7 +623,7 @@ static void irq_gc_shutdown(void) } } -static struct syscore_ops irq_gc_syscore_ops = { +static struct syscore_ops irq_gc_syscore_ops __ro_after_init = { .suspend = irq_gc_suspend, .resume = irq_gc_resume, .shutdown = irq_gc_shutdown, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index ee230063f033..0e5b723f710f 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -217,7 +217,7 @@ static int msi_domain_ops_check(struct irq_domain *domain, return 0; } -static struct msi_domain_ops msi_domain_ops_default = { +static struct msi_domain_ops msi_domain_ops_default __ro_after_init = { .get_hwirq = msi_domain_ops_get_hwirq, .msi_init = msi_domain_ops_init, .msi_check = msi_domain_ops_check, diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index cea1de0161f1..d6b889bed323 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -185,7 +185,7 @@ static void irq_pm_syscore_resume(void) resume_irqs(true); } -static struct syscore_ops irq_pm_syscore_ops = { +static struct syscore_ops irq_pm_syscore_ops __ro_after_init = { .resume = irq_pm_syscore_resume, };
Marked msi_domain_ops structs as __ro_after_init when called only during init. Marked syscore_ops structs as __ro_after_init when register_syscore_ops was called only during init. Most of the caller functions were already annotated as __init. unregister_syscore_ops() was never called on these syscore_ops. This protects the data structure from accidental corruption. Suggested-by: Kees Cook <keescook@chromium.org> Signed-off-by: Jess Frazelle <me@jessfraz.com> --- kernel/irq/generic-chip.c | 2 +- kernel/irq/msi.c | 2 +- kernel/irq/pm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) -- 2.11.0