diff mbox series

[v2,1/3] platform/x86: panasonic-laptop: Fix SINF array out of bounds accesses

Message ID 20240909113227.254470-1-hdegoede@redhat.com (mailing list archive)
State Accepted, archived
Headers show
Series [v2,1/3] platform/x86: panasonic-laptop: Fix SINF array out of bounds accesses | expand

Commit Message

Hans de Goede Sept. 9, 2024, 11:32 a.m. UTC
The panasonic laptop code in various places uses the SINF array with index
values of 0 - SINF_CUR_BRIGHT(0x0d) without checking that the SINF array
is big enough.

Not all panasonic laptops have this many SINF array entries, for example
the Toughbook CF-18 model only has 10 SINF array entries. So it only
supports the AC+DC brightness entries and mute.

Check that the SINF array has a minimum size which covers all AC+DC
brightness entries and refuse to load if the SINF array is smaller.

For higher SINF indexes hide the sysfs attributes when the SINF array
does not contain an entry for that attribute, avoiding show()/store()
accessing the array out of bounds and add bounds checking to the probe()
and resume() code accessing these.

Fixes: e424fb8cc4e6 ("panasonic-laptop: avoid overflow in acpi_pcc_hotkey_add()")
Cc: stable@vger.kernel.org
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
- Some panasonic laptops have only 10 SINF entries. Change the required
  minimum SQTY to SQTY > SINF_DC_CUR_BRIGHT and avoid the driver accessing
  higher indexes by adding num_sifr checks.
---
 drivers/platform/x86/panasonic-laptop.c | 49 ++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 10 deletions(-)

Comments

Andy Shevchenko Sept. 9, 2024, 12:06 p.m. UTC | #1
On Mon, Sep 09, 2024 at 01:32:25PM +0200, Hans de Goede wrote:
> The panasonic laptop code in various places uses the SINF array with index
> values of 0 - SINF_CUR_BRIGHT(0x0d) without checking that the SINF array
> is big enough.
> 
> Not all panasonic laptops have this many SINF array entries, for example
> the Toughbook CF-18 model only has 10 SINF array entries. So it only
> supports the AC+DC brightness entries and mute.
> 
> Check that the SINF array has a minimum size which covers all AC+DC
> brightness entries and refuse to load if the SINF array is smaller.
> 
> For higher SINF indexes hide the sysfs attributes when the SINF array
> does not contain an entry for that attribute, avoiding show()/store()
> accessing the array out of bounds and add bounds checking to the probe()
> and resume() code accessing these.

...

> +static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct acpi_device *acpi = to_acpi_device(dev);
> +	struct pcc_acpi *pcc = acpi_driver_data(acpi);

Isn't it the same as dev_get_drvdata()?

> +	if (attr == &dev_attr_mute.attr)
> +		return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0;
> +
> +	if (attr == &dev_attr_eco_mode.attr)
> +		return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0;
> +
> +	if (attr == &dev_attr_current_brightness.attr)
> +		return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0;
> +
> +	return attr->mode;
> +}

...

> +	/*
> +	 * pcc->sinf is expected to at least have the AC+DC brightness entries.
> +	 * Accesses to higher SINF entries are checked against num_sifr.
> +	 */
> +	if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {
> +		pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);

acpi_handle_err() ?

>  		return -ENODEV;
>  	}
Hans de Goede Sept. 9, 2024, 12:11 p.m. UTC | #2
Hi,

On 9/9/24 2:06 PM, Andy Shevchenko wrote:
> On Mon, Sep 09, 2024 at 01:32:25PM +0200, Hans de Goede wrote:
>> The panasonic laptop code in various places uses the SINF array with index
>> values of 0 - SINF_CUR_BRIGHT(0x0d) without checking that the SINF array
>> is big enough.
>>
>> Not all panasonic laptops have this many SINF array entries, for example
>> the Toughbook CF-18 model only has 10 SINF array entries. So it only
>> supports the AC+DC brightness entries and mute.
>>
>> Check that the SINF array has a minimum size which covers all AC+DC
>> brightness entries and refuse to load if the SINF array is smaller.
>>
>> For higher SINF indexes hide the sysfs attributes when the SINF array
>> does not contain an entry for that attribute, avoiding show()/store()
>> accessing the array out of bounds and add bounds checking to the probe()
>> and resume() code accessing these.
> 
> ...
> 
>> +static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
>> +{
>> +	struct device *dev = kobj_to_dev(kobj);
>> +	struct acpi_device *acpi = to_acpi_device(dev);
>> +	struct pcc_acpi *pcc = acpi_driver_data(acpi);
> 
> Isn't it the same as dev_get_drvdata()?

No I also thought so and I checked. It is not the same,
struct acpi_device has its own driver_data member, which
this gets.


> 
>> +	if (attr == &dev_attr_mute.attr)
>> +		return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0;
>> +
>> +	if (attr == &dev_attr_eco_mode.attr)
>> +		return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0;
>> +
>> +	if (attr == &dev_attr_current_brightness.attr)
>> +		return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0;
>> +
>> +	return attr->mode;
>> +}
> 
> ...
> 
>> +	/*
>> +	 * pcc->sinf is expected to at least have the AC+DC brightness entries.
>> +	 * Accesses to higher SINF entries are checked against num_sifr.
>> +	 */
>> +	if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {
>> +		pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);
> 
> acpi_handle_err() ?

The driver is using pr_err() already in 18 other places, so IMHO it is better
to be consistent and also use it here.

Regards,

Hans
Andy Shevchenko Sept. 9, 2024, 12:46 p.m. UTC | #3
On Mon, Sep 09, 2024 at 02:11:50PM +0200, Hans de Goede wrote:
> On 9/9/24 2:06 PM, Andy Shevchenko wrote:
> > On Mon, Sep 09, 2024 at 01:32:25PM +0200, Hans de Goede wrote:

...

> >> +static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
> >> +{
> >> +	struct device *dev = kobj_to_dev(kobj);
> >> +	struct acpi_device *acpi = to_acpi_device(dev);
> >> +	struct pcc_acpi *pcc = acpi_driver_data(acpi);
> > 
> > Isn't it the same as dev_get_drvdata()?
> 
> No I also thought so and I checked. It is not the same,
> struct acpi_device has its own driver_data member, which
> this gets.

Ouch, you are right! It's a bit confusing :-(

> >> +	if (attr == &dev_attr_mute.attr)
> >> +		return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0;
> >> +
> >> +	if (attr == &dev_attr_eco_mode.attr)
> >> +		return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0;
> >> +
> >> +	if (attr == &dev_attr_current_brightness.attr)
> >> +		return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0;
> >> +
> >> +	return attr->mode;
> >> +}

...

> >> +	/*
> >> +	 * pcc->sinf is expected to at least have the AC+DC brightness entries.
> >> +	 * Accesses to higher SINF entries are checked against num_sifr.
> >> +	 */
> >> +	if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {
> >> +		pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);
> > 
> > acpi_handle_err() ?
> 
> The driver is using pr_err() already in 18 other places, so IMHO it is better
> to be consistent and also use it here.

Fair enough, so can be material for a followup in the future.
Ilpo Järvinen Sept. 9, 2024, 5:54 p.m. UTC | #4
On Mon, 09 Sep 2024 13:32:25 +0200, Hans de Goede wrote:

> The panasonic laptop code in various places uses the SINF array with index
> values of 0 - SINF_CUR_BRIGHT(0x0d) without checking that the SINF array
> is big enough.
> 
> Not all panasonic laptops have this many SINF array entries, for example
> the Toughbook CF-18 model only has 10 SINF array entries. So it only
> supports the AC+DC brightness entries and mute.
> 
> [...]


Thank you for your contribution, it has been applied to my local
review-ilpo branch. Note it will show up in the public
platform-drivers-x86/review-ilpo branch only once I've pushed my
local branch there, which might take a while.

The list of commits applied:
[1/3] platform/x86: panasonic-laptop: Fix SINF array out of bounds accesses
      commit: f52e98d16e9bd7dd2b3aef8e38db5cbc9899d6a4
[2/3] platform/x86: panasonic-laptop: Allocate 1 entry extra in the sinf array
      commit: 33297cef3101d950cec0033a0dce0a2d2bd59999
[3/3] platform/x86: panasonic-laptop: Add support for programmable buttons
      (no commit info)

--
 i.
Ilpo Järvinen Sept. 9, 2024, 6:40 p.m. UTC | #5
On Mon, 9 Sep 2024, Ilpo Järvinen wrote:

> On Mon, 09 Sep 2024 13:32:25 +0200, Hans de Goede wrote:
> 
> > The panasonic laptop code in various places uses the SINF array with index
> > values of 0 - SINF_CUR_BRIGHT(0x0d) without checking that the SINF array
> > is big enough.
> > 
> > Not all panasonic laptops have this many SINF array entries, for example
> > the Toughbook CF-18 model only has 10 SINF array entries. So it only
> > supports the AC+DC brightness entries and mute.
> > 
> > [...]
> 
> 
> Thank you for your contribution, it has been applied to my local
> review-ilpo branch. Note it will show up in the public
> platform-drivers-x86/review-ilpo branch only once I've pushed my
> local branch there, which might take a while.
> 
> The list of commits applied:
> [1/3] platform/x86: panasonic-laptop: Fix SINF array out of bounds accesses
>       commit: f52e98d16e9bd7dd2b3aef8e38db5cbc9899d6a4
> [2/3] platform/x86: panasonic-laptop: Allocate 1 entry extra in the sinf array
>       commit: 33297cef3101d950cec0033a0dce0a2d2bd59999
> [3/3] platform/x86: panasonic-laptop: Add support for programmable buttons
>       (no commit info)

Hmpf, b4 messed this one up. Only patches 1-2 were applied and 3 should 
go through for-next.
Konstantin Ryabitsev Sept. 10, 2024, 2:26 p.m. UTC | #6
On Mon, Sep 09, 2024 at 09:40:36PM GMT, Ilpo Järvinen wrote:
> > Thank you for your contribution, it has been applied to my local
> > review-ilpo branch. Note it will show up in the public
> > platform-drivers-x86/review-ilpo branch only once I've pushed my
> > local branch there, which might take a while.
> > 
> > The list of commits applied:
> > [1/3] platform/x86: panasonic-laptop: Fix SINF array out of bounds accesses
> >       commit: f52e98d16e9bd7dd2b3aef8e38db5cbc9899d6a4
> > [2/3] platform/x86: panasonic-laptop: Allocate 1 entry extra in the sinf array
> >       commit: 33297cef3101d950cec0033a0dce0a2d2bd59999
> > [3/3] platform/x86: panasonic-laptop: Add support for programmable buttons
> >       (no commit info)
> 
> Hmpf, b4 messed this one up. Only patches 1-2 were applied and 3 should 
> go through for-next.

Hi:

This is a common gotcha and I'm hoping to make it less of a problem in the
future. The reason this happened is because you told b4 to retrieve the entire
patch series, but applied only a subset. We couldn't find a match for patch
3/3, but this often happens because maintainers make small tweaks to patch
contents, which skews b4 towards false-negatives instead of false-positives.

For the moment, the preferred way to avoid this problem is to tell b4 to
retrieve a subset of patches using `b4 am --cherry-pick 1-2` -- this way we
know that it was intended to be a subset and won't mention patch 3.

Hope this helps!

-K
diff mbox series

Patch

diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index cf845ee1c7b1..39044119d2a6 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -773,6 +773,24 @@  static DEVICE_ATTR_RW(dc_brightness);
 static DEVICE_ATTR_RW(current_brightness);
 static DEVICE_ATTR_RW(cdpower);
 
+static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (attr == &dev_attr_mute.attr)
+		return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0;
+
+	if (attr == &dev_attr_eco_mode.attr)
+		return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0;
+
+	if (attr == &dev_attr_current_brightness.attr)
+		return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0;
+
+	return attr->mode;
+}
+
 static struct attribute *pcc_sysfs_entries[] = {
 	&dev_attr_numbatt.attr,
 	&dev_attr_lcdtype.attr,
@@ -787,8 +805,9 @@  static struct attribute *pcc_sysfs_entries[] = {
 };
 
 static const struct attribute_group pcc_attr_group = {
-	.name	= NULL,		/* put in device directory */
-	.attrs	= pcc_sysfs_entries,
+	.name		= NULL,		/* put in device directory */
+	.attrs		= pcc_sysfs_entries,
+	.is_visible	= pcc_sysfs_is_visible,
 };
 
 
@@ -941,12 +960,15 @@  static int acpi_pcc_hotkey_resume(struct device *dev)
 	if (!pcc)
 		return -EINVAL;
 
-	acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute);
-	acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode);
+	if (pcc->num_sifr > SINF_MUTE)
+		acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute);
+	if (pcc->num_sifr > SINF_ECO_MODE)
+		acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode);
 	acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key);
 	acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, pcc->ac_brightness);
 	acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, pcc->dc_brightness);
-	acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness);
+	if (pcc->num_sifr > SINF_CUR_BRIGHT)
+		acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness);
 
 	return 0;
 }
@@ -963,8 +985,12 @@  static int acpi_pcc_hotkey_add(struct acpi_device *device)
 
 	num_sifr = acpi_pcc_get_sqty(device);
 
-	if (num_sifr < 0 || num_sifr > 255) {
-		pr_err("num_sifr out of range");
+	/*
+	 * pcc->sinf is expected to at least have the AC+DC brightness entries.
+	 * Accesses to higher SINF entries are checked against num_sifr.
+	 */
+	if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {
+		pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);
 		return -ENODEV;
 	}
 
@@ -1020,11 +1046,14 @@  static int acpi_pcc_hotkey_add(struct acpi_device *device)
 	acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0);
 	pcc->sticky_key = 0;
 
-	pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];
-	pcc->mute = pcc->sinf[SINF_MUTE];
 	pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
 	pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT];
-	pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
+	if (pcc->num_sifr > SINF_MUTE)
+		pcc->mute = pcc->sinf[SINF_MUTE];
+	if (pcc->num_sifr > SINF_ECO_MODE)
+		pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];
+	if (pcc->num_sifr > SINF_CUR_BRIGHT)
+		pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
 
 	/* add sysfs attributes */
 	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);