usb: smd: Fix crash during reset when RmNet is connected
When a QMI/RMNET control connection is established, a diag
reset causes a crash due to a NULL pointer dereference. The
SMD channel pointer is set to NULL upon USB cable disconnect,
but a queued work function might still try to access that pointer.
Fix the crash by checking for NULL pointers in both smd_* and in
USB gadget transport functions that could pass a closed handle.
CRs-fixed: 336850
Change-Id: I87cb109f3e7007efe15b5acc81180151dd2ef023
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index c237978..e3248ad 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -1931,42 +1931,77 @@
int smd_read(smd_channel_t *ch, void *data, int len)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->read(ch, data, len, 0);
}
EXPORT_SYMBOL(smd_read);
int smd_read_user_buffer(smd_channel_t *ch, void *data, int len)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->read(ch, data, len, 1);
}
EXPORT_SYMBOL(smd_read_user_buffer);
int smd_read_from_cb(smd_channel_t *ch, void *data, int len)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->read_from_cb(ch, data, len, 0);
}
EXPORT_SYMBOL(smd_read_from_cb);
int smd_write(smd_channel_t *ch, const void *data, int len)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 0);
}
EXPORT_SYMBOL(smd_write);
int smd_write_user_buffer(smd_channel_t *ch, const void *data, int len)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 1);
}
EXPORT_SYMBOL(smd_write_user_buffer);
int smd_read_avail(smd_channel_t *ch)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->read_avail(ch);
}
EXPORT_SYMBOL(smd_read_avail);
int smd_write_avail(smd_channel_t *ch)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->write_avail(ch);
}
EXPORT_SYMBOL(smd_write_avail);
@@ -1997,12 +2032,22 @@
int smd_cur_packet_size(smd_channel_t *ch)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return ch->current_packet;
}
EXPORT_SYMBOL(smd_cur_packet_size);
int smd_tiocmget(smd_channel_t *ch)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
return (ch->recv->fDSR ? TIOCM_DSR : 0) |
(ch->recv->fCTS ? TIOCM_CTS : 0) |
(ch->recv->fCD ? TIOCM_CD : 0) |
@@ -2016,6 +2061,11 @@
int
smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear)
{
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
if (set & TIOCM_DTR)
ch->send->fDSR = 1;
@@ -2040,6 +2090,11 @@
{
unsigned long flags;
+ if (!ch) {
+ pr_err("%s: Invalid channel specified\n", __func__);
+ return -ENODEV;
+ }
+
spin_lock_irqsave(&smd_lock, flags);
smd_tiocmset_from_cb(ch, set, clear);
spin_unlock_irqrestore(&smd_lock, flags);