From: "Éric Piel" <eric.piel@tremplin-utc.net>
To: Matthew Garrett <mjg@redhat.com>
Cc: Christian Lamparter <chunkeey@googlemail.com>,
LKML <linux-kernel@vger.kernel.org>,
platform-driver-x86@vger.kernel.org
Subject: [PATCH 01/10] lis3lv02d: avoid divide by zero due to unchecked
Date: Mon, 25 Jul 2011 17:16:23 +0200 [thread overview]
Message-ID: <4E2D88C7.30409@tremplin-utc.net> (raw)
In-Reply-To: <4E2D8858.8000900@tremplin-utc.net>
After an "unexpected" reboot, I found this Oops in my logs:
divide error: 0000 [#1] PREEMPT SMP=20
CPU 0=20
Modules linked in: lis3lv02d hp_wmi input_polldev [...]
Pid: 390, comm: modprobe Tainted: G C 2.6.39-rc7-wl+=20
RIP: 0010:[<ffffffffa014b427>] [<ffffffffa014b427>]
lis3lv02d_poweron+0x4e/0x94 [lis3lv02d]
RSP: 0018:ffff8801d6407cf8 EFLAGS: 00010246
RAX: 0000000000000bb8 RBX: ffffffffa014e000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffea00066e4708 RDI: ffff8801df002700
RBP: ffff8801d6407d18 R08: ffffea00066c5a30 R09: ffffffff812498c9
R10: ffff8801d7bfcea0 R11: ffff8801d7bfce10 R12: 0000000000000bb8
R13: 00000000ffffffda R14: ffffffffa0154120 R15: ffffffffa0154030
=46S: 00007fc0705db700(0000) GS:ffff8801dfa00000(0000) knlGS:0
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 00007f33549174f0 CR3: 00000001d65c9000 CR4: 00000000000406f0
Process modprobe (pid: 390, threadinfo ffff8801d6406000, task ffff8801d6b40=
000)
Stack:
ffffffffa0154120 62ffffffa0154030 ffffffffa014e000 00000000ffffffea
ffff8801d6407d58 ffffffffa014bcc1 0000000000000000 0000000000000048
ffff8801d8bae800 00000000ffffffea 00000000ffffffda ffffffffa0154120
Call Trace:
[<ffffffffa014bcc1>] lis3lv02d_init_device+0x1ce/0x496 [lis3lv02d]
[<ffffffffa01522ff>] lis3lv02d_add+0x10f/0x17c [hp_accel]
[<ffffffff81233e11>] acpi_device_probe+0x49/0x117
[...]
Code: 3a 75 06 80 4d ef 50 eb 04 80 4d ef 40 0f b6 55 ef be 21
00 00 00 48 89 df ff 53 18 44 8b 63 6c e8 3e fc ff ff 89 c1 44
89 e0 99 <f7> f9 89 c7 e8 93 82 ef e0 48 83 7b 30 00 74 2d 45
31 e4 80 7b=20
RIP [<ffffffffa014b427>] lis3lv02d_poweron+0x4e/0x94 [lis3lv02d]
RSP <ffff8801d6407cf8>
From my POV, it looks like the hardware is not working as expected
and returns a bogus data rate. The driver doesn't check the result
and directly uses it as some sort of divisor in some places:
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
Under this circumstances, this could very well cause the
"divide by zero" exception from above.
For now, I fixed it the easiest and most obvious way:
Check if the result is sane and if it isn't use a sane default
instead. I went for "100" in the latter case, simply because
/sys/devices/platform/lis3lv02d/rate returns it on a successful
boot.
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: Éric Piel <eric.piel@tremplin-utc.net>
---
drivers/misc/lis3lv02d/lis3lv02d.c | 36 ++++++++++++++++++++++++++++++------
drivers/misc/lis3lv02d/lis3lv02d.h | 2 +-
drivers/platform/x86/hp_accel.c | 3 +--
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index b928bc1..a9ee5f7 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -206,6 +206,18 @@ static int lis3lv02d_get_odr(void)
return lis3_dev.odrs[(ctrl >> shift)];
}
+static int lis3lv02d_get_pwron_wait(struct lis3lv02d *lis3)
+{
+ int div = lis3lv02d_get_odr();
+
+ if (WARN_ONCE(div == 0, "device returned spurious data"))
+ return -ENXIO;
+
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / div);
+ return 0;
+}
+
static int lis3lv02d_set_odr(int rate)
{
u8 ctrl;
@@ -266,7 +278,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
lis3->read(lis3, ctlreg, ®);
lis3->write(lis3, ctlreg, (reg | selftest));
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
/* Read directly to avoid axis remap */
x = lis3->read_data(lis3, OUTX);
@@ -275,7 +289,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
/* back to normal settings */
lis3->write(lis3, ctlreg, reg);
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
results[0] = x - lis3->read_data(lis3, OUTX);
results[1] = y - lis3->read_data(lis3, OUTY);
@@ -363,8 +379,9 @@ void lis3lv02d_poweroff(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
-void lis3lv02d_poweron(struct lis3lv02d *lis3)
+int lis3lv02d_poweron(struct lis3lv02d *lis3)
{
+ int err;
u8 reg;
lis3->init(lis3);
@@ -382,11 +399,14 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
- /* LIS3 power on delay is quite long */
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ err = lis3lv02d_get_pwron_wait(lis3);
+ if (err)
+ return err;
if (lis3->reg_ctrl)
lis3_context_restore(lis3);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
@@ -926,7 +946,11 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
atomic_set(&dev->wake_thread, 0);
lis3lv02d_add_fs(dev);
- lis3lv02d_poweron(dev);
+ err = lis3lv02d_poweron(dev);
+ if (err) {
+ lis3lv02d_remove_fs(dev);
+ return err;
+ }
if (dev->pm_dev) {
pm_runtime_set_active(dev->pm_dev);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index a193958..57c64bb 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -285,7 +285,7 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3);
int lis3lv02d_joystick_enable(void);
void lis3lv02d_joystick_disable(void);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
-void lis3lv02d_poweron(struct lis3lv02d *lis3);
+int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index 1b52d00..891e71f 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -354,8 +354,7 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
static int lis3lv02d_resume(struct acpi_device *device)
{
- lis3lv02d_poweron(&lis3_dev);
- return 0;
+ return lis3lv02d_poweron(&lis3_dev);
}
#else
#define lis3lv02d_suspend NULL
--
1.7.6
WARNING: multiple messages have this Message-ID (diff)
From: "Éric Piel" <eric.piel@tremplin-utc.net>
To: Matthew Garrett <mjg@redhat.com>
Cc: Christian Lamparter <chunkeey@googlemail.com>,
LKML <linux-kernel@vger.kernel.org>,
platform-driver-x86@vger.kernel.org
Subject: [PATCH 01/10] lis3lv02d: avoid divide by zero due to unchecked
Date: Mon, 25 Jul 2011 17:16:23 +0200 [thread overview]
Message-ID: <4E2D88C7.30409@tremplin-utc.net> (raw)
In-Reply-To: <4E2D8858.8000900@tremplin-utc.net>
After an "unexpected" reboot, I found this Oops in my logs:
divide error: 0000 [#1] PREEMPT SMP=20
CPU 0=20
Modules linked in: lis3lv02d hp_wmi input_polldev [...]
Pid: 390, comm: modprobe Tainted: G C 2.6.39-rc7-wl+=20
RIP: 0010:[<ffffffffa014b427>] [<ffffffffa014b427>]
lis3lv02d_poweron+0x4e/0x94 [lis3lv02d]
RSP: 0018:ffff8801d6407cf8 EFLAGS: 00010246
RAX: 0000000000000bb8 RBX: ffffffffa014e000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffea00066e4708 RDI: ffff8801df002700
RBP: ffff8801d6407d18 R08: ffffea00066c5a30 R09: ffffffff812498c9
R10: ffff8801d7bfcea0 R11: ffff8801d7bfce10 R12: 0000000000000bb8
R13: 00000000ffffffda R14: ffffffffa0154120 R15: ffffffffa0154030
=46S: 00007fc0705db700(0000) GS:ffff8801dfa00000(0000) knlGS:0
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 00007f33549174f0 CR3: 00000001d65c9000 CR4: 00000000000406f0
Process modprobe (pid: 390, threadinfo ffff8801d6406000, task ffff8801d6b40=
000)
Stack:
ffffffffa0154120 62ffffffa0154030 ffffffffa014e000 00000000ffffffea
ffff8801d6407d58 ffffffffa014bcc1 0000000000000000 0000000000000048
ffff8801d8bae800 00000000ffffffea 00000000ffffffda ffffffffa0154120
Call Trace:
[<ffffffffa014bcc1>] lis3lv02d_init_device+0x1ce/0x496 [lis3lv02d]
[<ffffffffa01522ff>] lis3lv02d_add+0x10f/0x17c [hp_accel]
[<ffffffff81233e11>] acpi_device_probe+0x49/0x117
[...]
Code: 3a 75 06 80 4d ef 50 eb 04 80 4d ef 40 0f b6 55 ef be 21
00 00 00 48 89 df ff 53 18 44 8b 63 6c e8 3e fc ff ff 89 c1 44
89 e0 99 <f7> f9 89 c7 e8 93 82 ef e0 48 83 7b 30 00 74 2d 45
31 e4 80 7b=20
RIP [<ffffffffa014b427>] lis3lv02d_poweron+0x4e/0x94 [lis3lv02d]
RSP <ffff8801d6407cf8>
>From my POV, it looks like the hardware is not working as expected
and returns a bogus data rate. The driver doesn't check the result
and directly uses it as some sort of divisor in some places:
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
Under this circumstances, this could very well cause the
"divide by zero" exception from above.
For now, I fixed it the easiest and most obvious way:
Check if the result is sane and if it isn't use a sane default
instead. I went for "100" in the latter case, simply because
/sys/devices/platform/lis3lv02d/rate returns it on a successful
boot.
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: Éric Piel <eric.piel@tremplin-utc.net>
---
drivers/misc/lis3lv02d/lis3lv02d.c | 36 ++++++++++++++++++++++++++++++------
drivers/misc/lis3lv02d/lis3lv02d.h | 2 +-
drivers/platform/x86/hp_accel.c | 3 +--
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index b928bc1..a9ee5f7 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -206,6 +206,18 @@ static int lis3lv02d_get_odr(void)
return lis3_dev.odrs[(ctrl >> shift)];
}
+static int lis3lv02d_get_pwron_wait(struct lis3lv02d *lis3)
+{
+ int div = lis3lv02d_get_odr();
+
+ if (WARN_ONCE(div == 0, "device returned spurious data"))
+ return -ENXIO;
+
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / div);
+ return 0;
+}
+
static int lis3lv02d_set_odr(int rate)
{
u8 ctrl;
@@ -266,7 +278,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
lis3->read(lis3, ctlreg, ®);
lis3->write(lis3, ctlreg, (reg | selftest));
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
/* Read directly to avoid axis remap */
x = lis3->read_data(lis3, OUTX);
@@ -275,7 +289,9 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
/* back to normal settings */
lis3->write(lis3, ctlreg, reg);
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ ret = lis3lv02d_get_pwron_wait(lis3);
+ if (ret)
+ goto fail;
results[0] = x - lis3->read_data(lis3, OUTX);
results[1] = y - lis3->read_data(lis3, OUTY);
@@ -363,8 +379,9 @@ void lis3lv02d_poweroff(struct lis3lv02d *lis3)
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
-void lis3lv02d_poweron(struct lis3lv02d *lis3)
+int lis3lv02d_poweron(struct lis3lv02d *lis3)
{
+ int err;
u8 reg;
lis3->init(lis3);
@@ -382,11 +399,14 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
- /* LIS3 power on delay is quite long */
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+ err = lis3lv02d_get_pwron_wait(lis3);
+ if (err)
+ return err;
if (lis3->reg_ctrl)
lis3_context_restore(lis3);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
@@ -926,7 +946,11 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
atomic_set(&dev->wake_thread, 0);
lis3lv02d_add_fs(dev);
- lis3lv02d_poweron(dev);
+ err = lis3lv02d_poweron(dev);
+ if (err) {
+ lis3lv02d_remove_fs(dev);
+ return err;
+ }
if (dev->pm_dev) {
pm_runtime_set_active(dev->pm_dev);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index a193958..57c64bb 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -285,7 +285,7 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3);
int lis3lv02d_joystick_enable(void);
void lis3lv02d_joystick_disable(void);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
-void lis3lv02d_poweron(struct lis3lv02d *lis3);
+int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index 1b52d00..891e71f 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -354,8 +354,7 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
static int lis3lv02d_resume(struct acpi_device *device)
{
- lis3lv02d_poweron(&lis3_dev);
- return 0;
+ return lis3lv02d_poweron(&lis3_dev);
}
#else
#define lis3lv02d_suspend NULL
--
1.7.6
next prev parent reply other threads:[~2011-07-25 15:16 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-07-25 15:14 [PATCH 00/10] lis3: various fixes and enhancements Éric Piel
2011-07-25 15:16 ` Éric Piel [this message]
2011-07-25 15:16 ` [PATCH 01/10] lis3lv02d: avoid divide by zero due to unchecked Éric Piel
2011-08-01 20:29 ` Andrew Morton
2011-08-01 21:11 ` Christian Lamparter
2011-08-01 21:29 ` Andrew Morton
2011-08-03 13:21 ` Éric Piel
2011-07-25 15:17 ` [PATCH 02/10] lis3: update maintainer information Éric Piel
2011-07-25 15:18 ` [PATCH 03/10] lis3: add support for HP EliteBook 2730p Éric Piel
2011-07-25 15:19 ` [PATCH 04/10] lis3: add support for HP EliteBook 8540w Éric Piel
2011-07-25 15:19 ` [PATCH 05/10] hp_accel: Add HP ProBook 655x Éric Piel
2011-07-25 15:20 ` [PATCH 06/10] CONFIG_HP_ACCEL: Fix help text Éric Piel
2011-07-25 15:21 ` [PATCH 07/10] lis3: Free regulators if probe() fails Éric Piel
2011-07-25 15:22 ` [PATCH 08/10] lis3: Change naming to consistent Éric Piel
2011-07-25 15:23 ` [PATCH 09/10] lis3: Change exported function to use given Éric Piel
2011-07-25 15:24 ` [PATCH 10/10] lis3: Remove the references to the global variable in core driver Éric Piel
2011-08-03 13:47 ` [PATCH 10/10 v2] " Éric Piel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E2D88C7.30409@tremplin-utc.net \
--to=eric.piel@tremplin-utc.net \
--cc=chunkeey@googlemail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mjg@redhat.com \
--cc=platform-driver-x86@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.