diff mbox

[v3,3/8] platform/x86: dell-wmi-smbios: Use Dell WMI descriptor check

Message ID bea602b33e27facc77ee850ef14dfa37f6894275.1506571188.git.mario.limonciello@dell.com (mailing list archive)
State Changes Requested, archived
Delegated to: Darren Hart
Headers show

Commit Message

Limonciello, Mario Sept. 28, 2017, 4:02 a.m. UTC
The Dell WMI descriptor check is used as an indication that WMI
calls are safe to run both when used with the notification
ASL/GUID pair as well as the SMBIOS calling ASL/GUID pair.

As some code in dell-wmi-smbios is already a prerequisite for
dell-wmi, move the code for performing the descriptor check into
dell-wmi-smbios and let both drivers use it from there.

Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
 drivers/platform/x86/dell-smbios.c | 81 ++++++++++++++++++++++++++++++++++++++
 drivers/platform/x86/dell-smbios.h |  3 ++
 drivers/platform/x86/dell-wmi.c    | 75 +----------------------------------
 3 files changed, 85 insertions(+), 74 deletions(-)

Comments

Darren Hart Sept. 30, 2017, 1:29 a.m. UTC | #1
On Wed, Sep 27, 2017 at 11:02:15PM -0500, Mario Limonciello wrote:
> The Dell WMI descriptor check is used as an indication that WMI
> calls are safe to run both when used with the notification
> ASL/GUID pair as well as the SMBIOS calling ASL/GUID pair.
> 
> As some code in dell-wmi-smbios is already a prerequisite for
> dell-wmi, move the code for performing the descriptor check into
> dell-wmi-smbios and let both drivers use it from there.
> 
> Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
> ---
...
> diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
...
>  
> +/*
> + * Descriptor buffer is 128 byte long and contains:
...
> +	if (obj->buffer.length != 128) {
> +		dev_err(&wdev->dev,
> +			"Dell descriptor buffer has invalid length (%d)\n",
> +			obj->buffer.length);

This seems odd. We call it an error (not a warning) if != 128, but we only abort
and return an error if it's < 16.

If it's an error, we should return an error code, if anything above 16 is
acceptable but 128 is preferred, the above should be a warning at best. (this
scenario seems unlikely).

> +		if (obj->buffer.length < 16) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +	}
> +	desc_buffer = (u32 *)obj->buffer.pointer;
> +
> +	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] != 0x494D5720)

This seems like it should be an || ?

(I see this is fixed in a later patch)

I would have suggested fixing it, then moving it - just from a pure ease of
review perspective. Perhaps not in the comment above that this is a verbatim
move, some fixes are coming in subsequent patches. This is worth noting for a
future develop who may be choosing what to backport.

>  static int dell_smbios_wmi_probe(struct wmi_device *wdev)
>  {
> +	int ret;
> +	u32 interface_version;

Declarations in order of decreasing line length please.

> @@ -236,6 +316,7 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev)
>  	if (!buffer)
>  		return -ENOMEM;
>  	bufferlen = sizeof(struct wmi_calling_interface_buffer);
> +

Stray whitespace, should have been in the previous patch I guess?
Limonciello, Mario Sept. 30, 2017, 7:48 p.m. UTC | #2
> -----Original Message-----
> From: Darren Hart [mailto:dvhart@infradead.org]
> Sent: Friday, September 29, 2017 8:29 PM
> To: Limonciello, Mario <Mario_Limonciello@Dell.com>
> Cc: Andy Shevchenko <andy.shevchenko@gmail.com>; LKML <linux-
> kernel@vger.kernel.org>; platform-driver-x86@vger.kernel.org; Andy Lutomirski
> <luto@kernel.org>; quasisec@google.com; pali.rohar@gmail.com
> Subject: Re: [PATCH v3 3/8] platform/x86: dell-wmi-smbios: Use Dell WMI
> descriptor check
> 
> On Wed, Sep 27, 2017 at 11:02:15PM -0500, Mario Limonciello wrote:
> > The Dell WMI descriptor check is used as an indication that WMI
> > calls are safe to run both when used with the notification
> > ASL/GUID pair as well as the SMBIOS calling ASL/GUID pair.
> >
> > As some code in dell-wmi-smbios is already a prerequisite for
> > dell-wmi, move the code for performing the descriptor check into
> > dell-wmi-smbios and let both drivers use it from there.
> >
> > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
> > ---
> ...
> > diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-
> smbios.c
> ...
> >
> > +/*
> > + * Descriptor buffer is 128 byte long and contains:
> ...
> > +	if (obj->buffer.length != 128) {
> > +		dev_err(&wdev->dev,
> > +			"Dell descriptor buffer has invalid length (%d)\n",
> > +			obj->buffer.length);
> 
> This seems odd. We call it an error (not a warning) if != 128, but we only abort
> and return an error if it's < 16.
> 
> If it's an error, we should return an error code, if anything above 16 is
> acceptable but 128 is preferred, the above should be a warning at best. (this
> scenario seems unlikely).

Hopefully the original author can speak up to the intentions here.  I would feel
that it should have errored out if it wasn't expected length too.

> 
> > +		if (obj->buffer.length < 16) {
> > +			ret = -EINVAL;
> > +			goto out;
> > +		}
> > +	}
> > +	desc_buffer = (u32 *)obj->buffer.pointer;
> > +
> > +	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] != 0x494D5720)
> 
> This seems like it should be an || ?
> 
> (I see this is fixed in a later patch)
> 
> I would have suggested fixing it, then moving it - just from a pure ease of
> review perspective. Perhaps not in the comment above that this is a verbatim
> move, some fixes are coming in subsequent patches. This is worth noting for a
> future develop who may be choosing what to backport.
> 

I'll re-order things so that the fixes come before the move.

> >  static int dell_smbios_wmi_probe(struct wmi_device *wdev)
> >  {
> > +	int ret;
> > +	u32 interface_version;
> 
> Declarations in order of decreasing line length please.
> 
> > @@ -236,6 +316,7 @@ static int dell_smbios_wmi_probe(struct wmi_device
> *wdev)
> >  	if (!buffer)
> >  		return -ENOMEM;
> >  	bufferlen = sizeof(struct wmi_calling_interface_buffer);
> > +
> 
> Stray whitespace, should have been in the previous patch I guess?
> 
> 
> --
> Darren Hart
> VMware Open Source Technology Center
Pali Rohár Sept. 30, 2017, 8:01 p.m. UTC | #3
On Saturday 30 September 2017 21:48:39 Mario.Limonciello@dell.com wrote:
> > > +/*
> > 
> > > + * Descriptor buffer is 128 byte long and contains:
> > ...
> > 
> > > +	if (obj->buffer.length != 128) {
> > > +		dev_err(&wdev->dev,
> > > +			"Dell descriptor buffer has invalid length (%d)\n",
> > > +			obj->buffer.length);
> > 
> > This seems odd. We call it an error (not a warning) if != 128, but
> > we only abort and return an error if it's < 16.
> > 
> > If it's an error, we should return an error code, if anything above
> > 16 is acceptable but 128 is preferred, the above should be a
> > warning at best. (this scenario seems unlikely).
> 
> Hopefully the original author can speak up to the intentions here.  I
> would feel that it should have errored out if it wasn't expected
> length too.

Code below access first 16 bytes of buffer. Therefore to prevent buffer 
overflow check for 16 bytes is needed.

But IIRC we decided to do not throw error and continue driver loading 
even when buffer length is not 128 (as expected by some Dell 
documentation) as it could be possible regression because driver itself 
does not depend on buffer length.

> > > +		if (obj->buffer.length < 16) {
> > > +			ret = -EINVAL;
> > > +			goto out;
> > > +		}
> > > +	}
> > > +	desc_buffer = (u32 *)obj->buffer.pointer;
> > > +
> > > +	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] !=
> > > 0x494D5720)
Andy Shevchenko Oct. 1, 2017, 8:43 a.m. UTC | #4
On Sat, Sep 30, 2017 at 4:29 AM, Darren Hart <dvhart@infradead.org> wrote:
> On Wed, Sep 27, 2017 at 11:02:15PM -0500, Mario Limonciello wrote:
>> The Dell WMI descriptor check is used as an indication that WMI
>> calls are safe to run both when used with the notification
>> ASL/GUID pair as well as the SMBIOS calling ASL/GUID pair.
>>
>> As some code in dell-wmi-smbios is already a prerequisite for
>> dell-wmi, move the code for performing the descriptor check into
>> dell-wmi-smbios and let both drivers use it from there.

>> +     if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] != 0x494D5720)
>
> This seems like it should be an || ?
>
> (I see this is fixed in a later patch)

Interesting, I proposed to use strncmp(), but it's gone again. Perhaps
I missed what is downside of that.
Limonciello, Mario Oct. 2, 2017, 2:15 p.m. UTC | #5
> -----Original Message-----

> From: Pali Rohár [mailto:pali.rohar@gmail.com]

> Sent: Saturday, September 30, 2017 3:01 PM

> To: Limonciello, Mario <Mario_Limonciello@Dell.com>

> Cc: dvhart@infradead.org; andy.shevchenko@gmail.com; linux-

> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org; luto@kernel.org;

> quasisec@google.com

> Subject: Re: [PATCH v3 3/8] platform/x86: dell-wmi-smbios: Use Dell WMI

> descriptor check

> 

> On Saturday 30 September 2017 21:48:39 Mario.Limonciello@dell.com wrote:

> > > > +/*

> > >

> > > > + * Descriptor buffer is 128 byte long and contains:

> > > ...

> > >

> > > > +	if (obj->buffer.length != 128) {

> > > > +		dev_err(&wdev->dev,

> > > > +			"Dell descriptor buffer has invalid length (%d)\n",

> > > > +			obj->buffer.length);

> > >

> > > This seems odd. We call it an error (not a warning) if != 128, but

> > > we only abort and return an error if it's < 16.

> > >

> > > If it's an error, we should return an error code, if anything above

> > > 16 is acceptable but 128 is preferred, the above should be a

> > > warning at best. (this scenario seems unlikely).

> >

> > Hopefully the original author can speak up to the intentions here.  I

> > would feel that it should have errored out if it wasn't expected

> > length too.

> 

> Code below access first 16 bytes of buffer. Therefore to prevent buffer

> overflow check for 16 bytes is needed.

> 

> But IIRC we decided to do not throw error and continue driver loading

> even when buffer length is not 128 (as expected by some Dell

> documentation) as it could be possible regression because driver itself

> does not depend on buffer length.

> 


So I'm intending to change this in my next patch series.  I feel it should throw an
error when the buffer length isn't 128.

My logic is that if you don't see the proper buffer size (or the proper header)
then how can you trust that the rest of the data is reliable?  This means the format
has changed or this isn't a real descriptor as expected by Dell (say some other vendor
that has cloned the GUID).  It's better to abort in this situation.

> > > > +		if (obj->buffer.length < 16) {

> > > > +			ret = -EINVAL;

> > > > +			goto out;

> > > > +		}

> > > > +	}

> > > > +	desc_buffer = (u32 *)obj->buffer.pointer;

> > > > +

> > > > +	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] !=

> > > > 0x494D5720)

> 

> --

> Pali Rohár

> pali.rohar@gmail.com
Pali Rohár Oct. 2, 2017, 2:37 p.m. UTC | #6
On Monday 02 October 2017 14:15:11 Mario.Limonciello@dell.com wrote:
> > -----Original Message-----
> > From: Pali Rohár [mailto:pali.rohar@gmail.com]
> > Sent: Saturday, September 30, 2017 3:01 PM
> > To: Limonciello, Mario <Mario_Limonciello@Dell.com>
> > Cc: dvhart@infradead.org; andy.shevchenko@gmail.com; linux-
> > kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org; luto@kernel.org;
> > quasisec@google.com
> > Subject: Re: [PATCH v3 3/8] platform/x86: dell-wmi-smbios: Use Dell WMI
> > descriptor check
> > 
> > On Saturday 30 September 2017 21:48:39 Mario.Limonciello@dell.com wrote:
> > > > > +/*
> > > >
> > > > > + * Descriptor buffer is 128 byte long and contains:
> > > > ...
> > > >
> > > > > +	if (obj->buffer.length != 128) {
> > > > > +		dev_err(&wdev->dev,
> > > > > +			"Dell descriptor buffer has invalid length (%d)\n",
> > > > > +			obj->buffer.length);
> > > >
> > > > This seems odd. We call it an error (not a warning) if != 128, but
> > > > we only abort and return an error if it's < 16.
> > > >
> > > > If it's an error, we should return an error code, if anything above
> > > > 16 is acceptable but 128 is preferred, the above should be a
> > > > warning at best. (this scenario seems unlikely).
> > >
> > > Hopefully the original author can speak up to the intentions here.  I
> > > would feel that it should have errored out if it wasn't expected
> > > length too.
> > 
> > Code below access first 16 bytes of buffer. Therefore to prevent buffer
> > overflow check for 16 bytes is needed.
> > 
> > But IIRC we decided to do not throw error and continue driver loading
> > even when buffer length is not 128 (as expected by some Dell
> > documentation) as it could be possible regression because driver itself
> > does not depend on buffer length.
> > 
> 
> So I'm intending to change this in my next patch series.  I feel it should throw an
> error when the buffer length isn't 128.
> 
> My logic is that if you don't see the proper buffer size (or the proper header)
> then how can you trust that the rest of the data is reliable?  This means the format
> has changed or this isn't a real descriptor as expected by Dell (say some other vendor
> that has cloned the GUID).  It's better to abort in this situation.

Error handling now is up to you -- Dell. You know the best how your
API/ABI behave.

I did that change to be fully backward compatible with possibility to
read interface version number (needed for event handling logic).

> > > > > +		if (obj->buffer.length < 16) {
> > > > > +			ret = -EINVAL;
> > > > > +			goto out;
> > > > > +		}
> > > > > +	}
> > > > > +	desc_buffer = (u32 *)obj->buffer.pointer;
> > > > > +
> > > > > +	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] !=
> > > > > 0x494D5720)
> > 
> > --
> > Pali Rohár
> > pali.rohar@gmail.com
diff mbox

Patch

diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
index 4472817ee045..5d793b012e5e 100644
--- a/drivers/platform/x86/dell-smbios.c
+++ b/drivers/platform/x86/dell-smbios.c
@@ -31,6 +31,7 @@ 
 #endif
 
 #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
 
 struct calling_interface_structure {
 	struct dmi_header header;
@@ -226,8 +227,87 @@  static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 	}
 }
 
+/*
+ * Descriptor buffer is 128 byte long and contains:
+ *
+ *       Name             Offset  Length  Value
+ * Vendor Signature          0       4    "DELL"
+ * Object Signature          4       4    " WMI"
+ * WMI Interface Version     8       4    <version>
+ * WMI buffer length        12       4    4096
+ */
+int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev, u32 *version)
+{
+	union acpi_object *obj = NULL;
+	struct wmi_device *desc_dev;
+	u32 *desc_buffer;
+	int ret;
+
+	desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
+	if (!desc_dev) {
+		dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
+		return -ENODEV;
+	}
+
+	obj = wmidev_block_query(desc_dev, 0);
+	if (!obj) {
+		dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (obj->buffer.length != 128) {
+		dev_err(&wdev->dev,
+			"Dell descriptor buffer has invalid length (%d)\n",
+			obj->buffer.length);
+		if (obj->buffer.length < 16) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+	desc_buffer = (u32 *)obj->buffer.pointer;
+
+	if (desc_buffer[0] != 0x4C4C4544 && desc_buffer[1] != 0x494D5720)
+		dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
+			8, desc_buffer);
+
+	if (desc_buffer[2] != 0 && desc_buffer[2] != 1)
+		dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
+			desc_buffer[2]);
+
+	if (desc_buffer[3] != 4096)
+		dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
+			desc_buffer[3]);
+
+	*version = desc_buffer[2];
+	ret = 0;
+
+	dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
+		*version);
+
+out:
+	kfree(obj);
+	put_device(&desc_dev->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_check_descriptor_buffer);
+
+
 static int dell_smbios_wmi_probe(struct wmi_device *wdev)
 {
+	int ret;
+	u32 interface_version;
+
+	ret = dell_wmi_check_descriptor_buffer(wdev, &interface_version);
+	if (ret)
+		return -ENODEV;
+
 	/* no longer need the SMI page */
 	free_page((unsigned long)buffer);
 
@@ -236,6 +316,7 @@  static int dell_smbios_wmi_probe(struct wmi_device *wdev)
 	if (!buffer)
 		return -ENOMEM;
 	bufferlen = sizeof(struct wmi_calling_interface_buffer);
+
 	wmi_dev = wdev;
 	return 0;
 }
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
index 2f6fce81ee69..be9ec1ccf8bf 100644
--- a/drivers/platform/x86/dell-smbios.h
+++ b/drivers/platform/x86/dell-smbios.h
@@ -17,6 +17,8 @@ 
 #ifndef _DELL_SMBIOS_H_
 #define _DELL_SMBIOS_H_
 
+#include <linux/wmi.h>
+
 struct notifier_block;
 
 /* If called through fallback SMI rather than WMI this structure will be
@@ -62,5 +64,6 @@  enum dell_laptop_notifier_actions {
 int dell_laptop_register_notifier(struct notifier_block *nb);
 int dell_laptop_unregister_notifier(struct notifier_block *nb);
 void dell_laptop_call_notifier(unsigned long action, void *data);
+int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev, u32 *version);
 
 #endif
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 1fbef560ca67..b3bb8cdd27bf 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -46,7 +46,6 @@  MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
 MODULE_LICENSE("GPL");
 
 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
-#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
 
 static bool wmi_requires_smbios_request;
 
@@ -617,78 +616,6 @@  static void dell_wmi_input_destroy(struct wmi_device *wdev)
 	input_unregister_device(priv->input_dev);
 }
 
-/*
- * Descriptor buffer is 128 byte long and contains:
- *
- *       Name             Offset  Length  Value
- * Vendor Signature          0       4    "DELL"
- * Object Signature          4       4    " WMI"
- * WMI Interface Version     8       4    <version>
- * WMI buffer length        12       4    4096
- */
-static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
-{
-	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
-	union acpi_object *obj = NULL;
-	struct wmi_device *desc_dev;
-	u32 *buffer;
-	int ret;
-
-	desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
-	if (!desc_dev) {
-		dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
-		return -ENODEV;
-	}
-
-	obj = wmidev_block_query(desc_dev, 0);
-	if (!obj) {
-		dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
-		ret = -EIO;
-		goto out;
-	}
-
-	if (obj->type != ACPI_TYPE_BUFFER) {
-		dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (obj->buffer.length != 128) {
-		dev_err(&wdev->dev,
-			"Dell descriptor buffer has invalid length (%d)\n",
-			obj->buffer.length);
-		if (obj->buffer.length < 16) {
-			ret = -EINVAL;
-			goto out;
-		}
-	}
-
-	buffer = (u32 *)obj->buffer.pointer;
-
-	if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
-		dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
-			8, buffer);
-
-	if (buffer[2] != 0 && buffer[2] != 1)
-		dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
-			buffer[2]);
-
-	if (buffer[3] != 4096)
-		dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
-			buffer[3]);
-
-	priv->interface_version = buffer[2];
-	ret = 0;
-
-	dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
-		priv->interface_version);
-
-out:
-	kfree(obj);
-	put_device(&desc_dev->dev);
-	return ret;
-}
-
 /*
  * According to Dell SMBIOS documentation:
  *
@@ -732,7 +659,7 @@  static int dell_wmi_probe(struct wmi_device *wdev)
 		return -ENOMEM;
 	dev_set_drvdata(&wdev->dev, priv);
 
-	err = dell_wmi_check_descriptor_buffer(wdev);
+	err = dell_wmi_check_descriptor_buffer(wdev, &priv->interface_version);
 	if (err)
 		return err;