diff mbox

rtc: armada38x: add __ro_after_init to armada38x_rtc_ops

Message ID 20170103215421.GN14217@n2100.armlinux.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Russell King (Oracle) Jan. 3, 2017, 9:54 p.m. UTC
On Tue, Jan 03, 2017 at 09:31:18PM +0000, Russell King - ARM Linux wrote:
> On Tue, Jan 03, 2017 at 01:18:29PM -0800, Kees Cook wrote:
> > On Mon, Jan 2, 2017 at 6:06 AM, Russell King - ARM Linux
> > <linux@armlinux.org.uk> wrote:
> > > On Mon, Dec 26, 2016 at 05:01:02PM +0530, Bhumika Goyal wrote:
> > >> The object armada38x_rtc_ops of type rtc_class_ops structure is not
> > >> modified after getting initialized by armada38x_rtc_probe. Apart from
> > >> getting referenced in init it is also passed as an argument to the function
> > >> devm_rtc_device_register but this argument is of type const struct
> > >> rtc_class_ops *. Therefore add __ro_after_init to its declaration.
> > >
> > > What I'd prefer here is for the structure to be duplicated, with one
> > > copy having the alarm methods and one which does not.  Both can then
> > > be made "const" (so placed into the read-only section at link time)
> > > and the probe function select between the two.
> > >
> > > I think that's a cleaner and better solution, even though it's
> > > slightly larger.
> > >
> > > I'm not a fan of __ro_after_init being used where other solutions are
> > > possible.
> > 
> > Can the pointer that points to the struct rtc_class_ops be made ro_after_init?
> 
> It's passed into the RTC core code, and probably stored in some dynamically
> allocated object, so probably no.  It's the same class of problem as every
> file_operations pointer in the kernel, or the thousand other operations
> structure pointers that a running kernel has.

For the elimination of doubt, this is what I meant in my original email.
As you can see, there's nothing to be marked as __ro_after_init anymore.

 drivers/rtc/rtc-armada38x.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

Comments

Julia Lawall Jan. 4, 2017, 10:57 a.m. UTC | #1
On Tue, 3 Jan 2017, Russell King - ARM Linux wrote:

> On Tue, Jan 03, 2017 at 09:31:18PM +0000, Russell King - ARM Linux wrote:
> > On Tue, Jan 03, 2017 at 01:18:29PM -0800, Kees Cook wrote:
> > > On Mon, Jan 2, 2017 at 6:06 AM, Russell King - ARM Linux
> > > <linux@armlinux.org.uk> wrote:
> > > > On Mon, Dec 26, 2016 at 05:01:02PM +0530, Bhumika Goyal wrote:
> > > >> The object armada38x_rtc_ops of type rtc_class_ops structure is not
> > > >> modified after getting initialized by armada38x_rtc_probe. Apart from
> > > >> getting referenced in init it is also passed as an argument to the function
> > > >> devm_rtc_device_register but this argument is of type const struct
> > > >> rtc_class_ops *. Therefore add __ro_after_init to its declaration.
> > > >
> > > > What I'd prefer here is for the structure to be duplicated, with one
> > > > copy having the alarm methods and one which does not.  Both can then
> > > > be made "const" (so placed into the read-only section at link time)
> > > > and the probe function select between the two.
> > > >
> > > > I think that's a cleaner and better solution, even though it's
> > > > slightly larger.
> > > >
> > > > I'm not a fan of __ro_after_init being used where other solutions are
> > > > possible.
> > >
> > > Can the pointer that points to the struct rtc_class_ops be made ro_after_init?
> >
> > It's passed into the RTC core code, and probably stored in some dynamically
> > allocated object, so probably no.  It's the same class of problem as every
> > file_operations pointer in the kernel, or the thousand other operations
> > structure pointers that a running kernel has.

I'm not sure to understand the question and the response.  A quick check
with grep suggests that most rtc_class_ops pointers are already const.
There seem to be just some instances in specific drivers that are not.

julia

>
> For the elimination of doubt, this is what I meant in my original email.
> As you can see, there's nothing to be marked as __ro_after_init anymore.
>
>  drivers/rtc/rtc-armada38x.c | 24 +++++++++++++++++-------
>  1 file changed, 17 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
> index 9a3f2a6f512e..a4166ccfce36 100644
> --- a/drivers/rtc/rtc-armada38x.c
> +++ b/drivers/rtc/rtc-armada38x.c
> @@ -202,7 +202,7 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
>  	return IRQ_HANDLED;
>  }
>
> -static struct rtc_class_ops armada38x_rtc_ops = {
> +static const struct rtc_class_ops armada38x_rtc_ops = {
>  	.read_time = armada38x_rtc_read_time,
>  	.set_time = armada38x_rtc_set_time,
>  	.read_alarm = armada38x_rtc_read_alarm,
> @@ -210,8 +210,15 @@ static struct rtc_class_ops armada38x_rtc_ops = {
>  	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
>  };
>
> +static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
> +	.read_time = armada38x_rtc_read_time,
> +	.set_time = armada38x_rtc_set_time,
> +	.read_alarm = armada38x_rtc_read_alarm,
> +};
> +
>  static __init int armada38x_rtc_probe(struct platform_device *pdev)
>  {
> +	const struct rtc_class_ops *ops;
>  	struct resource *res;
>  	struct armada38x_rtc *rtc;
>  	int ret;
> @@ -242,19 +249,22 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
>  				0, pdev->name, rtc) < 0) {
>  		dev_warn(&pdev->dev, "Interrupt not available.\n");
>  		rtc->irq = -1;
> +	}
> +	platform_set_drvdata(pdev, rtc);
> +
> +	if (rtc->irq != -1) {
> +		device_init_wakeup(&pdev->dev, 1);
> +		ops = &armada38x_rtc_ops;
> +	} else {
>  		/*
>  		 * If there is no interrupt available then we can't
>  		 * use the alarm
>  		 */
> -		armada38x_rtc_ops.set_alarm = NULL;
> -		armada38x_rtc_ops.alarm_irq_enable = NULL;
> +		ops = &armada38x_rtc_ops_noirq;
>  	}
> -	platform_set_drvdata(pdev, rtc);
> -	if (rtc->irq != -1)
> -		device_init_wakeup(&pdev->dev, 1);
>
>  	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
> -					&armada38x_rtc_ops, THIS_MODULE);
> +						ops, THIS_MODULE);
>  	if (IS_ERR(rtc->rtc_dev)) {
>  		ret = PTR_ERR(rtc->rtc_dev);
>  		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
>
> --
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
> according to speedtest.net.
>
Alexandre Belloni Jan. 4, 2017, 11:07 a.m. UTC | #2
On 04/01/2017 at 11:57:00 +0100, Julia Lawall wrote :
> 
> 
> On Tue, 3 Jan 2017, Russell King - ARM Linux wrote:
> 
> > On Tue, Jan 03, 2017 at 09:31:18PM +0000, Russell King - ARM Linux wrote:
> > > On Tue, Jan 03, 2017 at 01:18:29PM -0800, Kees Cook wrote:
> > > > On Mon, Jan 2, 2017 at 6:06 AM, Russell King - ARM Linux
> > > > <linux@armlinux.org.uk> wrote:
> > > > > On Mon, Dec 26, 2016 at 05:01:02PM +0530, Bhumika Goyal wrote:
> > > > >> The object armada38x_rtc_ops of type rtc_class_ops structure is not
> > > > >> modified after getting initialized by armada38x_rtc_probe. Apart from
> > > > >> getting referenced in init it is also passed as an argument to the function
> > > > >> devm_rtc_device_register but this argument is of type const struct
> > > > >> rtc_class_ops *. Therefore add __ro_after_init to its declaration.
> > > > >
> > > > > What I'd prefer here is for the structure to be duplicated, with one
> > > > > copy having the alarm methods and one which does not.  Both can then
> > > > > be made "const" (so placed into the read-only section at link time)
> > > > > and the probe function select between the two.
> > > > >
> > > > > I think that's a cleaner and better solution, even though it's
> > > > > slightly larger.
> > > > >
> > > > > I'm not a fan of __ro_after_init being used where other solutions are
> > > > > possible.
> > > >
> > > > Can the pointer that points to the struct rtc_class_ops be made ro_after_init?
> > >
> > > It's passed into the RTC core code, and probably stored in some dynamically
> > > allocated object, so probably no.  It's the same class of problem as every
> > > file_operations pointer in the kernel, or the thousand other operations
> > > structure pointers that a running kernel has.
> 
> I'm not sure to understand the question and the response.  A quick check
> with grep suggests that most rtc_class_ops pointers are already const.
> There seem to be just some instances in specific drivers that are not.
> 

The question was whether the point to the rtc_class_ops could be made
__ro_after_init. And Russell is right, it is pointed to by the ops
pointer in a struct rtc_device and that struct is dynamically allocated
in rtc_device_register().
Julia Lawall Jan. 4, 2017, 11:43 a.m. UTC | #3
> The question was whether the point to the rtc_class_ops could be made
> __ro_after_init. And Russell is right, it is pointed to by the ops
> pointer in a struct rtc_device and that struct is dynamically allocated
> in rtc_device_register().

OK, I think it's a terminology issue.  You mean the structure that
contains the pointer, and not the pointer itself, which is already const.

thanks,
julia
Russell King (Oracle) Jan. 4, 2017, 12:14 p.m. UTC | #4
On Wed, Jan 04, 2017 at 12:43:32PM +0100, Julia Lawall wrote:
> > The question was whether the point to the rtc_class_ops could be made
> > __ro_after_init. And Russell is right, it is pointed to by the ops
> > pointer in a struct rtc_device and that struct is dynamically allocated
> > in rtc_device_register().
> 
> OK, I think it's a terminology issue.  You mean the structure that
> contains the pointer, and not the pointer itself, which is already const.

That statement is really ambiguous, and really doesn't help the cause -
we have several structures here which contain pointers and it's far from
clear which you're talking about:

- The armada38x_rtc_ops and armada38x_rtc_ops_noirq structures contain
  pointers to functions.
- The dynamically allocated struct rtc_device contains an ops pointer,
  which will point at one or other of these two structures.

Now, as soon as we make armada38x_rtc_ops and armada38x_rtc_ops_noirq
const, if the pointer is passed through any function call where the
argument is not also marked const, or is assigned to a pointer that is
not marked const (without an explicit cast), the compiler will complain.
Remember that a const pointer (iow, const void *ptr) is just a hint to
the compiler that "ptr" _may_ point at read-only data, and dereferences
of the pointer are not allowed to write - it's just syntactic checking.

Given that this is stuff we should all know, I'm not quite sure what
people are getting in a tiz over... I'm finding it worrying that I'm
even writing this mail, reviewing this stuff!  _Really_ worried that
Kees even brought it up in the first place - I suspect that came from
a misunderstanding of my suggestion which is why I later provided the
suggestion in patch form.

What I suggested, and what my patch does is:

1. It places both the armada38x_rtc_ops and armada38x_rtc_ops_noirq
   structures into the .rodata section, which will be protected from
   writes by hardware when appropriate kernel options are enabled.

2. The driver does _not_ store a local pointer to this memory at a
   static address which could be subsequently modified (*).

3. The only pointer to this memory is during driver initialisation
   (which may well reside in a CPU register only) before being passed
   to the RTC subsystem.

4. The RTC subsystem dynamically allocates a struct rtc_device
   structure (in rtc_device_register()) where it eventually stores
   this pointer.  This pointer is already marked const.  This structure
   contains read/write data, and can't be marked read-only, just in the
   same way as "struct file" can't be.

The whole __ro_after_init thing is completely irrelevant and a total
distraction at this point - there is nothing that you could add a
__ro_after_init annotation to after my patch in regard of these ops
structures.

* - however, a compiler may decide to store the addresses of these
structures as a literal constant near the function, but with RONX
protection for the .text section, this memory is also read-only, and
so can't be modified.
Julia Lawall Jan. 4, 2017, 12:23 p.m. UTC | #5
On Wed, 4 Jan 2017, Russell King - ARM Linux wrote:

> On Wed, Jan 04, 2017 at 12:43:32PM +0100, Julia Lawall wrote:
> > > The question was whether the point to the rtc_class_ops could be made
> > > __ro_after_init. And Russell is right, it is pointed to by the ops
> > > pointer in a struct rtc_device and that struct is dynamically allocated
> > > in rtc_device_register().
> >
> > OK, I think it's a terminology issue.  You mean the structure that
> > contains the pointer, and not the pointer itself, which is already const.
>
> That statement is really ambiguous, and really doesn't help the cause -
> we have several structures here which contain pointers and it's far from
> clear which you're talking about:
>
> - The armada38x_rtc_ops and armada38x_rtc_ops_noirq structures contain
>   pointers to functions.
> - The dynamically allocated struct rtc_device contains an ops pointer,
>   which will point at one or other of these two structures.
>
> Now, as soon as we make armada38x_rtc_ops and armada38x_rtc_ops_noirq
> const, if the pointer is passed through any function call where the
> argument is not also marked const, or is assigned to a pointer that is
> not marked const (without an explicit cast), the compiler will complain.
> Remember that a const pointer (iow, const void *ptr) is just a hint to
> the compiler that "ptr" _may_ point at read-only data, and dereferences
> of the pointer are not allowed to write - it's just syntactic checking.
>
> Given that this is stuff we should all know, I'm not quite sure what
> people are getting in a tiz over... I'm finding it worrying that I'm
> even writing this mail, reviewing this stuff!  _Really_ worried that
> Kees even brought it up in the first place - I suspect that came from
> a misunderstanding of my suggestion which is why I later provided the
> suggestion in patch form.
>
> What I suggested, and what my patch does is:
>
> 1. It places both the armada38x_rtc_ops and armada38x_rtc_ops_noirq
>    structures into the .rodata section, which will be protected from
>    writes by hardware when appropriate kernel options are enabled.
>
> 2. The driver does _not_ store a local pointer to this memory at a
>    static address which could be subsequently modified (*).
>
> 3. The only pointer to this memory is during driver initialisation
>    (which may well reside in a CPU register only) before being passed
>    to the RTC subsystem.
>
> 4. The RTC subsystem dynamically allocates a struct rtc_device
>    structure (in rtc_device_register()) where it eventually stores
>    this pointer.  This pointer is already marked const.  This structure
>    contains read/write data, and can't be marked read-only, just in the
>    same way as "struct file" can't be.
>
> The whole __ro_after_init thing is completely irrelevant and a total
> distraction at this point - there is nothing that you could add a
> __ro_after_init annotation to after my patch in regard of these ops
> structures.
>
> * - however, a compiler may decide to store the addresses of these
> structures as a literal constant near the function, but with RONX
> protection for the .text section, this memory is also read-only, and
> so can't be modified.

Thanks for the explanation.  I understood the patch, just not Kees's
question.

Basically, the strategy of the patch is that one may consider it
preferable to duplicate the structure for the different alternatives,
rather than use __ro_after_init.  Perhaps if the structure were larger,
then __ro_after_init would be a better choice?

thanks,
julia
Russell King (Oracle) Jan. 4, 2017, 1:06 p.m. UTC | #6
On Wed, Jan 04, 2017 at 01:23:41PM +0100, Julia Lawall wrote:
> Basically, the strategy of the patch is that one may consider it
> preferable to duplicate the structure for the different alternatives,
> rather than use __ro_after_init.  Perhaps if the structure were larger,
> then __ro_after_init would be a better choice?

It depends on not just the size, but how many members need to be
modified, and obviously whether there are likely to be more than one
user of the structure as well.

So I'd say __ro_after_init rarely makes sense for an operations
structure - the only case I can see is:

- a large structure
- only a small number of elements need to be modified
- it is only single-use

which is probably quite rare - this one falls into two out of those
three.

There's another consideration (imho) too - we may wish, at a later
date, to make .text and .rodata both read-only from the start of the
kernel to harden the kernel against possibly init-time exploitation.
(Think about a buggy built-in driver with emulated hardware - much the
same problem that Kees is trying to address in one of his recent patch
sets but with hotplugged hardware while a screen-saver is active.)
Having function pointers in .rodata rather than the ro-after-init
section would provide better protection.
Julia Lawall Jan. 4, 2017, 1:41 p.m. UTC | #7
On Wed, 4 Jan 2017, Russell King - ARM Linux wrote:

> On Wed, Jan 04, 2017 at 01:23:41PM +0100, Julia Lawall wrote:
> > Basically, the strategy of the patch is that one may consider it
> > preferable to duplicate the structure for the different alternatives,
> > rather than use __ro_after_init.  Perhaps if the structure were larger,
> > then __ro_after_init would be a better choice?
>
> It depends on not just the size, but how many members need to be
> modified, and obviously whether there are likely to be more than one
> user of the structure as well.
>
> So I'd say __ro_after_init rarely makes sense for an operations
> structure - the only case I can see is:
>
> - a large structure
> - only a small number of elements need to be modified
> - it is only single-use
>
> which is probably quite rare - this one falls into two out of those
> three.
>
> There's another consideration (imho) too - we may wish, at a later
> date, to make .text and .rodata both read-only from the start of the
> kernel to harden the kernel against possibly init-time exploitation.
> (Think about a buggy built-in driver with emulated hardware - much the
> same problem that Kees is trying to address in one of his recent patch
> sets but with hotplugged hardware while a screen-saver is active.)
> Having function pointers in .rodata rather than the ro-after-init
> section would provide better protection.

OK, thanks for the explanations.

julia
Kees Cook Jan. 4, 2017, 9:53 p.m. UTC | #8
On Wed, Jan 4, 2017 at 5:06 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Wed, Jan 04, 2017 at 01:23:41PM +0100, Julia Lawall wrote:
>> Basically, the strategy of the patch is that one may consider it
>> preferable to duplicate the structure for the different alternatives,
>> rather than use __ro_after_init.  Perhaps if the structure were larger,
>> then __ro_after_init would be a better choice?
>
> It depends on not just the size, but how many members need to be
> modified, and obviously whether there are likely to be more than one
> user of the structure as well.
>
> So I'd say __ro_after_init rarely makes sense for an operations
> structure - the only case I can see is:
>
> - a large structure
> - only a small number of elements need to be modified
> - it is only single-use
>
> which is probably quite rare - this one falls into two out of those
> three.
>
> There's another consideration (imho) too - we may wish, at a later
> date, to make .text and .rodata both read-only from the start of the
> kernel to harden the kernel against possibly init-time exploitation.
> (Think about a buggy built-in driver with emulated hardware - much the
> same problem that Kees is trying to address in one of his recent patch
> sets but with hotplugged hardware while a screen-saver is active.)
> Having function pointers in .rodata rather than the ro-after-init
> section would provide better protection.

Agreed: I'd much prefer things just be const. :) As to my confusing
question, I hadn't looked at how where the pointers to the structure
was being stored, so I was just asking if it, too, could be const,
which it can't, and that's fine here.

-Kees
diff mbox

Patch

diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index 9a3f2a6f512e..a4166ccfce36 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -202,7 +202,7 @@  static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static struct rtc_class_ops armada38x_rtc_ops = {
+static const struct rtc_class_ops armada38x_rtc_ops = {
 	.read_time = armada38x_rtc_read_time,
 	.set_time = armada38x_rtc_set_time,
 	.read_alarm = armada38x_rtc_read_alarm,
@@ -210,8 +210,15 @@  static struct rtc_class_ops armada38x_rtc_ops = {
 	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
 };
 
+static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
+	.read_time = armada38x_rtc_read_time,
+	.set_time = armada38x_rtc_set_time,
+	.read_alarm = armada38x_rtc_read_alarm,
+};
+
 static __init int armada38x_rtc_probe(struct platform_device *pdev)
 {
+	const struct rtc_class_ops *ops;
 	struct resource *res;
 	struct armada38x_rtc *rtc;
 	int ret;
@@ -242,19 +249,22 @@  static __init int armada38x_rtc_probe(struct platform_device *pdev)
 				0, pdev->name, rtc) < 0) {
 		dev_warn(&pdev->dev, "Interrupt not available.\n");
 		rtc->irq = -1;
+	}
+	platform_set_drvdata(pdev, rtc);
+
+	if (rtc->irq != -1) {
+		device_init_wakeup(&pdev->dev, 1);
+		ops = &armada38x_rtc_ops;
+	} else {
 		/*
 		 * If there is no interrupt available then we can't
 		 * use the alarm
 		 */
-		armada38x_rtc_ops.set_alarm = NULL;
-		armada38x_rtc_ops.alarm_irq_enable = NULL;
+		ops = &armada38x_rtc_ops_noirq;
 	}
-	platform_set_drvdata(pdev, rtc);
-	if (rtc->irq != -1)
-		device_init_wakeup(&pdev->dev, 1);
 
 	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
-					&armada38x_rtc_ops, THIS_MODULE);
+						ops, THIS_MODULE);
 	if (IS_ERR(rtc->rtc_dev)) {
 		ret = PTR_ERR(rtc->rtc_dev);
 		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);