irq: set {msi_domain,syscore}_ops as __ro_after_init
PATCH v2 1/5 irq: set {msi_domain,syscore}_ops as __ro_after_init
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.
--- kernel/irq/generic-chip.c | 2 +- kernel/irq/msi.c | 2 +- kernel/irq/pm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
kernel/irq/generic-chip.c +1 -1
@@ -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,
kernel/irq/msi.c +1 -1
@@ -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,
kernel/irq/pm.c +1 -1
@@ -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, };
--
2.11.0
PATCH v2 2/5 time: mark syscore_ops as __ro_after_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 ops.
This protects the data structure from accidental corruption.
--- kernel/time/sched_clock.c | 2 +- kernel/time/timekeeping.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
kernel/time/sched_clock.c +1 -1
@@ -289,7 +289,7 @@ static void sched_clock_resume(void) rd->read_sched_clock = cd.actual_read_sched_clock; } -static struct syscore_ops sched_clock_ops = { +static struct syscore_ops sched_clock_ops__ro_after_init = { .suspend = sched_clock_suspend, .resume = sched_clock_resume, };
kernel/time/timekeeping.c +1 -1
@@ -1756,7 +1756,7 @@ int timekeeping_suspend(void) }/* sysfs resume/suspend bits for timekeeping */ -static struct syscore_ops timekeeping_syscore_ops = { +static struct syscore_ops timekeeping_syscore_ops__ro_after_init = { .resume = timekeeping_resume, .suspend = timekeeping_suspend, };
--
2.11.0
Re: [PATCH v2 2/5] time: mark syscore_ops as __ro_after_init
Quoted 9 lines · Jess Frazelle
Marked
syscore_opsstructs as__ro_after_initwhenregister_syscore_opswas called only during init. Most of the caller functions were already annotated as__init.unregister_syscore_ops()was never called on these ops.
This protects the data structure from accidental corruption.Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>Acked-by Rik van Riel <[email protected]>
Thanks for sending this out. Looks reasonable to me. I'll queue it for testing, targeting for 4.12.
thanks
-john
Re: [PATCH v2 2/5] time: mark syscore_ops as __ro_after_init
Quoted 7 lines · John Stultz
Quoted 6 lines · Jess Frazelle
Marked
syscore_opsstructs as__ro_after_initwhenregister_syscore_opswas called only during init. Most of the caller functions were already annotated as__init.unregister_syscore_ops()was never called on these ops.
This protects the data structure from accidental corruption.
Quoted 6 lines
Quoted 3 lines
Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>Acked-by Rik van Riel <[email protected]>Thanks for sending this out. Looks reasonable to me. I'll queue it for testing, targeting for 4.12.
NAK. See:
http://lkml.kernel.org/r/alpine.DEB.2.20.1702110948030.3734@nanos
Thanks,
tglx
PATCH v2 3/5 pci: set msi_domain_ops as __ro_after_init
In reply to Jess FrazelleMarked msi_domain_ops structs as __ro_after_init when called only during init. This protects the data structure from accidental corruption.
--- drivers/pci/host/pci-hyperv.c | 2 +- drivers/pci/host/vmd.c | 2 +- drivers/pci/msi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
drivers/pci/host/pci-hyperv.c +1 -1
@@ -958,7 +958,7 @@ static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info,return arg->msi_hwirq; } -static struct msi_domain_ops hv_msi_ops = { +static struct msi_domain_ops hv_msi_ops__ro_after_init = { .get_hwirq = hv_msi_domain_ops_get_hwirq, .msi_prepare = pci_msi_prepare, .set_desc = pci_msi_set_desc,
drivers/pci/host/vmd.c +1 -1
@@ -253,7 +253,7 @@ static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) arg->desc = desc; } -static struct msi_domain_ops vmd_msi_domain_ops = { +static struct msi_domain_ops vmd_msi_domain_ops__ro_after_init = { .get_hwirq = vmd_get_hwirq, .msi_init = vmd_msi_init, .msi_free = vmd_msi_free,
drivers/pci/msi.c +1 -1
@@ -1413,7 +1413,7 @@ static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,#define pci_msi_domain_set_desc NULL#endif -static struct msi_domain_ops pci_msi_domain_ops_default = { +static struct msi_domain_ops pci_msi_domain_ops_default__ro_after_init = { .set_desc = pci_msi_domain_set_desc, .msi_check = pci_msi_domain_check_cap, .handle_error = pci_msi_domain_handle_error,
--
2.11.0
RE: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 14 lines
-----Original Message-----
From: Jess Frazelle [mailto:[email protected]]
Sent: Friday, February 10, 2017 5:38 PM
To: KY Srinivasan <[email protected]>; Haiyang Zhang <[email protected]>; Stephen Hemminger <[email protected]>; Bjorn Helgaas <[email protected]>; Keith Busch <[email protected]>; open list:Hyper-V CORE AND DRIVERS <[email protected]>; open list:PCI SUBSYSTEM <linux- [email protected]>; open list <[email protected]>
Cc: [email protected]; Jess Frazelle <[email protected]>
Subject: [PATCH v2 3/5] pci: setmsi_domain_opsas__ro_after_init
Quoted 6 lines
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init.
This protects the data structure from accidental corruption.Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>
K. Y
Quoted 51 lines
--- drivers/pci/host/pci-hyperv.c | 2 +- drivers/pci/host/vmd.c | 2 +- drivers/pci/msi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)drivers/pci/host/pci-hyperv.c +1
-1@@ -958,7 +958,7 @@ static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info, return arg->msi_hwirq; } -static struct msi_domain_ops hv_msi_ops = { +static struct msi_domain_ops hv_msi_ops__ro_after_init = { .get_hwirq = hv_msi_domain_ops_get_hwirq, .msi_prepare = pci_msi_prepare,drivers/pci/host/pci-hyperv.c +0
-0.set_desc = pci_msi_set_desc,drivers/pci/host/vmd.c +1
-1@@ -253,7 +253,7 @@ static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) arg->desc = desc; } -static struct msi_domain_ops vmd_msi_domain_ops = { +static struct msi_domain_ops vmd_msi_domain_ops__ro_after_init = { .get_hwirq = vmd_get_hwirq, .msi_init = vmd_msi_init,drivers/pci/host/vmd.c +0
-0.msi_free = vmd_msi_free,drivers/pci/msi.c +1
-1@@ -1413,7 +1413,7 @@ static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,#define pci_msi_domain_set_desc NULL#endif -static struct msi_domain_ops pci_msi_domain_ops_default = { +static struct msi_domain_ops pci_msi_domain_ops_default__ro_after_init = { .set_desc = pci_msi_domain_set_desc,drivers/pci/msi.c +0
-1.msi_check = pci_msi_domain_check_cap, .handle_error = pci_msi_domain_handle_error, --2.11.0
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
In reply to Jess FrazelleQuoted 8 lines · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init. This protects the data structure from accidental corruption.Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>drivers/pci/host/pci-hyperv.c | 2 +- drivers/pci/host/vmd.c | 2 +-
Ok for vmd driver.
Quoted 2 lines
drivers/pci/msi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
In reply to Jess Frazelle[+cc Kees, Thomas, Marc]
Hi Jess,
Thanks for the patch!
Quoted 9 lines · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init. This protects the data structure from accidental corruption.Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>--- drivers/pci/host/pci-hyperv.c | 2 +- drivers/pci/host/vmd.c | 2 +- drivers/pci/msi.c | 2 +-
I understand the value of __ro_after_init, but I'm not certain about sprinkling it around in seemingly random places because it's hard to know where to put it and whether we put it in all the right places.
How did you choose these three files to change? There are other instances of struct msi_domain_ops that use MSI_FLAG_USE_DEF_DOM_OPS. Should they be changed, too? If not, is there a rule to figure out which ones should be made __ro_after_init?
I wonder if adding __ro_after_init is really the best solution. I looked at VMD to see how vmd_msi_domain_ops is updated. Here are the definitions:
static struct msi_domain_ops vmd_msi_domain_ops = {
.get_hwirq = vmd_get_hwirq,
.msi_init = vmd_msi_init,
...
};
static struct msi_domain_info vmd_msi_domain_info = {
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_PCI_MSIX,
.ops = &vmd_msi_domain_ops,
...
};Both vmd_msi_domain_ops and vmd_msi_domain_info are statically initialized, but not completely. Then we pass a pointer to pci_msi_create_irq_domain(), which fills in defaults for some of the function pointers that weren't statically initialized:
vmd_enable_domain()
pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info, ...)
if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
pci_msi_domain_update_dom_ops(info)
if (ops->set_desc == NULL)
ops->msi_check = pci_msi_domain_check_capWe know at build-time what all the function pointers will be, so in principle we should be able to make the struct const, which would be even better than __ro_after_init.
For example, we could require that callers set every function pointer before calling pci_msi_create_irq_domain(), using the default ones (pci_msi_domain_set_desc, pci_msi_domain_check_cap, pci_msi_domain_handle_error) if it doesn't need to override them, e.g.,
static struct msi_domain_ops vmd_msi_domain_ops = {
.get_hwirq = vmd_get_hwirq,
.msi_check = pci_msi_domain_check_cap,
};Or we could leave NULL pointers in the structure and have the code that calls through the function pointers check for NULL and call the default itself, e.g.,
if (ops->msi_check)
ops->msi_check(...)
else
pci_msi_domain_check_cap(...)It looks like the "USE_DEF_OPS" framework was added by Jiang Liu with the commits below. I would CC: him for his thoughts, but I don't have a current email address.
aeeb59657c35 ("genirq: Provide default callbacks for msi_domain_ops") 3878eaefb89a ("PCI/MSI: Enhance core to support hierarchy irqdomain")
Bjorn
Quoted 44 lines
3 files changed, 3 insertions(+), 3 deletions(-)drivers/pci/host/pci-hyperv.c +1
-1@@ -958,7 +958,7 @@ static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info,return arg->msi_hwirq; } -static struct msi_domain_ops hv_msi_ops = { +static struct msi_domain_ops hv_msi_ops__ro_after_init = { .get_hwirq = hv_msi_domain_ops_get_hwirq, .msi_prepare = pci_msi_prepare, .set_desc = pci_msi_set_desc,drivers/pci/host/vmd.c +1
-1@@ -253,7 +253,7 @@ static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) arg->desc = desc; } -static struct msi_domain_ops vmd_msi_domain_ops = { +static struct msi_domain_ops vmd_msi_domain_ops__ro_after_init = { .get_hwirq = vmd_get_hwirq, .msi_init = vmd_msi_init, .msi_free = vmd_msi_free,drivers/pci/msi.c +1
-2@@ -1413,7 +1413,7 @@ static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,#define pci_msi_domain_set_desc NULL#endif -static struct msi_domain_ops pci_msi_domain_ops_default = { +static struct msi_domain_ops pci_msi_domain_ops_default__ro_after_init = { .set_desc = pci_msi_domain_set_desc, .msi_check = pci_msi_domain_check_cap, .handle_error = pci_msi_domain_handle_error, --2.11.0
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 79 lines · Bjorn Helgaas
[+cc Kees, Thomas, Marc]
Hi Jess,
Thanks for the patch!
Quoted 9 lines · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init. This protects the data structure from accidental corruption.Suggested-by Kees Cook <[email protected]>Signed-off-by Jess Frazelle <[email protected]>--- drivers/pci/host/pci-hyperv.c | 2 +- drivers/pci/host/vmd.c | 2 +- drivers/pci/msi.c | 2 +-I understand the value of
__ro_after_init, but I'm not certain about sprinkling it around in seemingly random places because it's hard to know where to put it and whether we put it in all the right places.How did you choose these three files to change? There are other instances of struct
msi_domain_opsthat useMSI_FLAG_USE_DEF_DOM_OPS. Should they be changed, too? If not, is there a rule to figure out which ones should be made__ro_after_init?I wonder if adding
__ro_after_initis really the best solution. I looked at VMD to see howvmd_msi_domain_opsis updated. Here are the definitions:static struct msi_domain_ops vmd_msi_domain_ops = { .get_hwirq = vmd_get_hwirq, .msi_init = vmd_msi_init, ... }; static struct msi_domain_info vmd_msi_domain_info = { .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_PCI_MSIX, .ops = &vmd_msi_domain_ops, ... };Both
vmd_msi_domain_opsandvmd_msi_domain_infoare statically initialized, but not completely. Then we pass a pointer topci_msi_create_irq_domain(), which fills in defaults for some of the function pointers that weren't statically initialized:
vmd_enable_domain()pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info, ...) if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) pci_msi_domain_update_dom_ops(info) if (ops->set_desc == NULL) ops->msi_check = pci_msi_domain_check_capWe know at build-time what all the function pointers will be, so in principle we should be able to make the struct const, which would be even better than
__ro_after_init.For example, we could require that callers set every function pointer before calling
pci_msi_create_irq_domain(), using the default ones (pci_msi_domain_set_desc,pci_msi_domain_check_cap,pci_msi_domain_handle_error) if it doesn't need to override them, e.g.,static struct msi_domain_ops vmd_msi_domain_ops = { .get_hwirq = vmd_get_hwirq, .msi_check = pci_msi_domain_check_cap, };Or we could leave NULL pointers in the structure and have the code that calls through the function pointers check for NULL and call the default itself, e.g.,
if (
ops->msi_check)ops->msi_check(...) else pci_msi_domain_check_cap(...)
Quoted 6 lines
It looks like the "
USE_DEF_OPS" framework was added by Jiang Liu with the commits below. I would CC: him for his thoughts, but I don't have a current email address.
aeeb59657c35("genirq: Provide default callbacks formsi_domain_ops")3878eaefb89a("PCI/MSI: Enhance core to support hierarchy irqdomain")
If we can do const, that would be preferred. That's generally easier to reason about. I ended up doing this to the cdrom ops structure just the other day:
http://www.openwall.com/lists/kernel-hardening/2017/02/14/2
-Kees
Signature · 2 lines
Kees Cook Pixel Security
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
In reply to Bjorn HelgaasQuoted 3 lines · Bjorn Helgaas
We know at build-time what all the function pointers will be, so in principle we should be able to make the struct const, which would be even better than
__ro_after_init.
Not everywhere unfortunately. In some instances it's a runtime decision, but yes, they could be fixed. But there is a downside in doing this. See below.
Quoted 15 lines
For example, we could require that callers set every function pointer before calling
pci_msi_create_irq_domain(), using the default ones (pci_msi_domain_set_desc,pci_msi_domain_check_cap,pci_msi_domain_handle_error) if it doesn't need to override them, e.g.,static struct msi_domain_ops vmd_msi_domain_ops = { .get_hwirq = vmd_get_hwirq, .msi_check = pci_msi_domain_check_cap, };Or we could leave NULL pointers in the structure and have the code that calls through the function pointers check for NULL and call the default itself, e.g.,
Quoted 8 lines
if (
ops->msi_check)ops->msi_check(...) else pci_msi_domain_check_cap(...)It looks like the "
USE_DEF_OPS" framework was added by Jiang Liu with the commits below. I would CC: him for his thoughts, but I don't have a current email address.
Me neither :(
I think I suggested to Jiang to do that 'update with default functions' to
- avoid exporting the world and some more
- have the flexibility to add new functions to the ops w/o updating a gazillion of existing usage sites, which has saved us lots of chaising in the last years
- avoid the if (ops->ptr) ops->ptr(); else default_fn(); constructs all over the place.
I admit I did not think about the fact that this makes the structs non const.
Mopping that up by exporting the default functions and setting all the function pointers is tedious and requires a full tree sweep when we add new stuff. There's also code shared between PCI/platform/DT based stuff, so that becomes interesting.
Doing the if (ops->ptr) ops->ptr() else default_fn(); dance should be simpler to pull off. There are not that many sites to look at, but then we have some of the GICv3 code using the domain ops out of core.
For now doing the __ro_after_init is definitely the simplest and fastest solution to tighten these statically allocated structures.
I have a look with Marc, what can be done in the long run.
Thanks,
tglx
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 11 lines · Thomas Gleixner
I think I suggested to Jiang to do that 'update with default functions' to
- avoid exporting the world and some more
- have the flexibility to add new functions to the ops w/o updating a gazillion of existing usage sites, which has saved us lots of chaising in the last years
- avoid the if (
ops->ptr)ops->ptr(); elsedefault_fn(); constructs all over the place.
Quoted 7 lines
I admit I did not think about the fact that this makes the structs non const.
Mopping that up by exporting the default functions and setting all the function pointers is tedious and requires a full tree sweep when we add new stuff. There's also code shared between PCI/platform/DT based stuff, so that becomes interesting.
It's legal to initialize a field multiple times, and the last one takes precedence, so doing this might at least avoid the full tree sweeps:
static struct msi_domain_ops vmd_msi_domain_ops = {
MSI_DOMAIN_DEFAULT_OPS,
.get_hwirq = vmd_get_hwirq,
};The functions referenced by MSI_DOMAIN_DEFAULT_OPS would still have to be exported, though.
Quoted 6 lines
Doing the if (
ops->ptr)ops->ptr()elsedefault_fn(); dance should be simpler to pull off. There are not that many sites to look at, but then we have some of the GICv3 code using the domain ops out of core.For now doing the
__ro_after_initis definitely the simplest and fastest solution to tighten these statically allocated structures.
I'm OK with __ro_after_init, at least as an interim solution.
I do think it would be good to audit all the uses of MSI_FLAG_USE_DEF_DOM_OPS and MSI_FLAG_USE_DEF_CHIP_OPS, since they seem to be the primary indicator of when the struct might be modified. I suspect we could add __ro_after_init to more than just pci-hyperv.c, vmd.c, and msi.c
Bjorn
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 25 lines · Bjorn Helgaas
Quoted 11 lines · Thomas Gleixner
I think I suggested to Jiang to do that 'update with default functions' to
- avoid exporting the world and some more
- have the flexibility to add new functions to the ops w/o updating a gazillion of existing usage sites, which has saved us lots of chaising in the last years
- avoid the if (
ops->ptr)ops->ptr(); elsedefault_fn(); constructs all over the place.Quoted 7 lines
I admit I did not think about the fact that this makes the structs non const.
Mopping that up by exporting the default functions and setting all the function pointers is tedious and requires a full tree sweep when we add new stuff. There's also code shared between PCI/platform/DT based stuff, so that becomes interesting.
It's legal to initialize a field multiple times, and the last one takes precedence, so doing this might at least avoid the full tree sweeps:
Quoted 7 lines
static struct msi_domain_ops vmd_msi_domain_ops = { MSI_DOMAIN_DEFAULT_OPS, .get_hwirq = vmd_get_hwirq, };The functions referenced by
MSI_DOMAIN_DEFAULT_OPSwould still have to be exported, though.
Hmm, that'd work. Though it will fall apart for those pieces where we share code across backends. But I did not yet go through all the places and check them.
Quoted 7 lines
Quoted 6 lines
Doing the if (
ops->ptr)ops->ptr()elsedefault_fn(); dance should be simpler to pull off. There are not that many sites to look at, but then we have some of the GICv3 code using the domain ops out of core.For now doing the
__ro_after_initis definitely the simplest and fastest solution to tighten these statically allocated structures.
Quoted 7 lines
I'm OK with
__ro_after_init, at least as an interim solution.I do think it would be good to audit all the uses of
MSI_FLAG_USE_DEF_DOM_OPSandMSI_FLAG_USE_DEF_CHIP_OPS, since they seem to be the primary indicator of when the struct might be modified. I suspect we could add__ro_after_initto more than justpci-hyperv.c,vmd.c, andmsi.c
Agreed. I have it on my radar.
Thanks,
tglx
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 47 lines · Thomas Gleixner
Quoted 25 lines · Bjorn Helgaas
Quoted 11 lines · Thomas Gleixner
I think I suggested to Jiang to do that 'update with default functions' to
- avoid exporting the world and some more
- have the flexibility to add new functions to the ops w/o updating a gazillion of existing usage sites, which has saved us lots of chaising in the last years
- avoid the if (
ops->ptr)ops->ptr(); elsedefault_fn(); constructs all over the place.Quoted 7 lines
I admit I did not think about the fact that this makes the structs non const.
Mopping that up by exporting the default functions and setting all the function pointers is tedious and requires a full tree sweep when we add new stuff. There's also code shared between PCI/platform/DT based stuff, so that becomes interesting.
It's legal to initialize a field multiple times, and the last one takes precedence, so doing this might at least avoid the full tree sweeps:
Quoted 7 lines
static struct msi_domain_ops vmd_msi_domain_ops = { MSI_DOMAIN_DEFAULT_OPS, .get_hwirq = vmd_get_hwirq, };The functions referenced by
MSI_DOMAIN_DEFAULT_OPSwould still have to be exported, though.Hmm, that'd work. Though it will fall apart for those pieces where we share code across backends. But I did not yet go through all the places and check them.
Quoted 9 lines
Quoted 6 lines
Doing the if (
ops->ptr)ops->ptr()elsedefault_fn(); dance should be simpler to pull off. There are not that many sites to look at, but then we have some of the GICv3 code using the domain ops out of core.For now doing the
__ro_after_initis definitely the simplest and fastest solution to tighten these statically allocated structures.I'm OK with
__ro_after_init, at least as an interim solution.
Quoted 7 lines
Quoted 5 lines
I do think it would be good to audit all the uses of
MSI_FLAG_USE_DEF_DOM_OPSandMSI_FLAG_USE_DEF_CHIP_OPS, since they seem to be the primary indicator of when the struct might be modified. I suspect we could add__ro_after_initto more than justpci-hyperv.c,vmd.c, andmsi.cAgreed. I have it on my radar.
This seems like a worthwhile change, so I don't want to just drop this patch. But if we're going to do something, I'd like to do it everywhere that it makes sense, all at the same time.
It looks like the v2 series was split up by subsystem, which is fine with me. I'll happily apply the PCI parts (or ack them, since it might make sense to apply all of them via the same non-PCI tree).
But I *would* like to include the following users of MSI_FLAG_USE_DEF_DOM_OPS and MSI_FLAG_USE_DEF_CHIP_OPS at the same time (or explain why __ro_after_init won't work for them):
pci-xgene-msi.c
pcie-altera-msi.c
pcie-iproc-msi.c
pcie-xilinx-nwl.c
Bjorn
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
I can update the patch series, sorry haven't had much time to devote to this the past few weeks, but will update in the next day.
Quoted 74 lines · Bjorn Helgaas
Quoted 47 lines · Thomas Gleixner
Quoted 25 lines · Bjorn Helgaas
Quoted 11 lines · Thomas Gleixner
I think I suggested to Jiang to do that 'update with default functions' to
- avoid exporting the world and some more
- have the flexibility to add new functions to the ops w/o updating a gazillion of existing usage sites, which has saved us lots of chaising in the last years
- avoid the if (
ops->ptr)ops->ptr(); elsedefault_fn(); constructs all over the place.Quoted 7 lines
I admit I did not think about the fact that this makes the structs non const.
Mopping that up by exporting the default functions and setting all the function pointers is tedious and requires a full tree sweep when we add new stuff. There's also code shared between PCI/platform/DT based stuff, so that becomes interesting.
It's legal to initialize a field multiple times, and the last one takes precedence, so doing this might at least avoid the full tree sweeps:
Quoted 7 lines
static struct msi_domain_ops vmd_msi_domain_ops = { MSI_DOMAIN_DEFAULT_OPS, .get_hwirq = vmd_get_hwirq, };The functions referenced by
MSI_DOMAIN_DEFAULT_OPSwould still have to be exported, though.Hmm, that'd work. Though it will fall apart for those pieces where we share code across backends. But I did not yet go through all the places and check them.
Quoted 9 lines
Quoted 6 lines
Doing the if (
ops->ptr)ops->ptr()elsedefault_fn(); dance should be simpler to pull off. There are not that many sites to look at, but then we have some of the GICv3 code using the domain ops out of core.For now doing the
__ro_after_initis definitely the simplest and fastest solution to tighten these statically allocated structures.I'm OK with
__ro_after_init, at least as an interim solution.Quoted 7 lines
Quoted 5 lines
I do think it would be good to audit all the uses of
MSI_FLAG_USE_DEF_DOM_OPSandMSI_FLAG_USE_DEF_CHIP_OPS, since they seem to be the primary indicator of when the struct might be modified. I suspect we could add__ro_after_initto more than justpci-hyperv.c,vmd.c, andmsi.cAgreed. I have it on my radar.
This seems like a worthwhile change, so I don't want to just drop this patch. But if we're going to do something, I'd like to do it everywhere that it makes sense, all at the same time.
It looks like the v2 series was split up by subsystem, which is fine with me. I'll happily apply the PCI parts (or ack them, since it might make sense to apply all of them via the same non-PCI tree).
But I *would* like to include the following users of
MSI_FLAG_USE_DEF_DOM_OPSandMSI_FLAG_USE_DEF_CHIP_OPSat the same time (or explain why__ro_after_initwon't work for them):
pci-xgene-msi.c
pcie-altera-msi.c
pcie-iproc-msi.c
pcie-xilinx-nwl.cBjorn
Signature · 3 lines
Jessie Frazelle 4096R / D4C4 DD60 0D66 F65A 8EFC 511E 18F3 685C 0022 BFF3 pgp.mit.edu
Re: [PATCH v2 3/5] pci: set msi_domain_ops as __ro_after_init
Quoted 2 lines · Jessica Frazelle
I can update the patch series, sorry haven't had much time to devote to this the past few weeks, but will update in the next day.
Thanks, Jessica! No problem, I know the feeling :)
Bjorn
PATCH v2 4/5 staging: set msi_domain_ops as __ro_after_init
In reply to Jess FrazelleMarked msi_domain_ops structs as __ro_after_init when called only during init. This protects the data structure from accidental corruption.
--- drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c +1 -1
@@ -51,7 +51,7 @@ static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,return msi_info->ops->msi_prepare (msi_domain->parent, dev, nvec, info); } -static struct msi_domain_ops its_fsl_mc_msi_ops = { +static struct msi_domain_ops its_fsl_mc_msi_ops__ro_after_init = { .msi_prepare = its_fsl_mc_msi_prepare, };
--
2.11.0
PATCH v2 5/5 x86: set msi_domain_ops as __ro_after_init
In reply to Jess FrazelleMarked msi_domain_ops structs as __ro_after_init when called only during init. This protects the data structure from accidental corruption.
--- arch/x86/kernel/apic/msi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/kernel/apic/msi.c +3 -3
@@ -121,7 +121,7 @@ void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) }EXPORT_SYMBOL_GPL (pci_msi_set_desc); -static struct msi_domain_ops pci_msi_domain_ops = { +static struct msi_domain_ops pci_msi_domain_ops__ro_after_init = { .get_hwirq = pci_msi_get_hwirq, .msi_prepare = pci_msi_prepare, .set_desc = pci_msi_set_desc,
@@ -207,7 +207,7 @@ static int dmar_msi_init(struct irq_domain *domain,return 0 ; } -static struct msi_domain_ops dmar_msi_domain_ops = { +static struct msi_domain_ops dmar_msi_domain_ops__ro_after_init = { .get_hwirq = dmar_msi_get_hwirq, .msi_init = dmar_msi_init, };
@@ -304,7 +304,7 @@ static void hpet_msi_free(struct irq_domain *domain,irq_clear_status_flags (virq,IRQ_MOVE_PCNTXT ); } -static struct msi_domain_ops hpet_msi_domain_ops = { +static struct msi_domain_ops hpet_msi_domain_ops__ro_after_init = { .get_hwirq = hpet_msi_get_hwirq, .msi_init = hpet_msi_init, .msi_free = hpet_msi_free,
--
2.11.0
Quoted 6 lines · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init. Markedsyscore_opsstructs as__ro_after_initwhenregister_syscore_opswas called only during init. Most of the caller functions were already annotated as__init.unregister_syscore_ops()was never called on thesesyscore_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.
kernel/irq/generic-chip.c static void irq_gc_shutdown(void) · line 629
Quoted 5 lines
kernel/irq/generic-chip.c +1
-1-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.
kernel/irq/msi.c static int msi_domain_ops_check(struct irq_domain *domain, · line 220
Quoted 2 lines
kernel/irq/msi.c +1
-1-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
Quoted 48 lines · Thomas Gleixner
Quoted 6 lines · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called only during init. Markedsyscore_opsstructs as__ro_after_initwhenregister_syscore_opswas called only during init. Most of the caller functions were already annotated as__init.unregister_syscore_ops()was never called on thesesyscore_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_opscan be made read only after init because they are only updated in the registration case.struct
syscore_opscan 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.
kernel/irq/generic-chip.c static void irq_gc_shutdown(void) · line 629
Quoted 5 lines
kernel/irq/generic-chip.c +1
-1-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_opscan be made__ro_after_initat all.Assume the following:
last_init_function()
register_syscore_ops(&a_ops)list_add(&a_ops->node, list);
apply_ro_after_init()
//a_opsare now read only
Quoted 8 lines
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
Quoted 26 lines · Thomas Gleixner
Quoted 1 line · Jess Frazelle
Marked
msi_domain_opsstructs as__ro_after_initwhen called onlyduring init.
Quoted 1 line
Marked
syscore_opsstructs as__ro_after_initwhen
register_syscore_opswasQuoted 1 line
called only during init. Most of the caller functions were already
annotated as
Quoted 3 lines
__init.unregister_syscore_ops()was never called on thesesyscore_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.
Quoted 7 lines
struct
msi_domain_opscan be made read only after init because they are
only updated in the registration case.struct
syscore_opscan 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.
Quoted 20 lines
Emphasis on WOULD, See below.
kernel/irq/generic-chip.c static void irq_gc_shutdown(void) · line 629
Quoted 5 lines
kernel/irq/generic-chip.c +1
-1-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_opscan be made__ro_after_initat all.Assume the following:
last_init_function()
register_syscore_ops(&a_ops)
list_add(&a_ops->node, list);
apply_ro_after_init()
//a_opsare now read only
Quoted 8 lines
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 linkb_opstoa_ops.The same is true for cpuhotunplug operations.
This makes sense. Will remove.
Quoted 11 lines
Review
Quoted 3 lines
(quoted diff) +1
-1-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_initinstead of analysing howmsi_domain_ops_defaultis used.
Quoted 4 lines
msi_domain_ops_defaultare 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.
Quoted 4 lines
Thanks,
tglx
Quoted 4 lines · Jess Frazelle
Quoted 1 line · Thomas Gleixner
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.
Quoted 56 lines · Thomas Gleixner
Quoted 4 lines · Jess Frazelle
Quoted 1 line · Thomas Gleixner
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 filestatic struct syscore_ops ops __ro_after_init = { .... };int
__initfoo(){ 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_syscorecallbacks: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