2016-03-11 11:19:26

by Loic Poulain

[permalink] [raw]
Subject: [PATCH] Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data

HCI_UART_PROTO_SET flag is set before hci_uart_set_proto call. If we
receive data from tty layer during this procedure, proto pointer may
not be assigned yet, leading to null pointer dereference in rx method
hci_uart_tty_receive.

This patch fixes this by setting the HCI_UART_PROTO_SET flag once proto
is fully initialized and introduces HCI_UART_PROTO_BUSY flag to avoid
concurrent call to set_proto. We could have fixed this with a mutex as
well.

Signed-off-by: Loic Poulain <[email protected]>
---
drivers/bluetooth/hci_ldisc.c | 16 +++++++++-------
drivers/bluetooth/hci_uart.h | 1 +
2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index c00168a..fab2641 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -638,6 +638,7 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
return err;

hu->proto = p;
+ set_bit(HCI_UART_PROTO_SET, &hu->flags);

err = hci_uart_register_dev(hu);
if (err) {
@@ -692,14 +693,15 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,

switch (cmd) {
case HCIUARTSETPROTO:
- if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
- err = hci_uart_set_proto(hu, arg);
- if (err) {
- clear_bit(HCI_UART_PROTO_SET, &hu->flags);
- return err;
- }
- } else
+ if (test_and_set_bit(HCI_UART_PROTO_BUSY, &hu->flags))
return -EBUSY;
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ err = -EBUSY;
+ else
+ err = hci_uart_set_proto(hu, arg);
+ clear_bit(HCI_UART_PROTO_BUSY, &hu->flags);
+ if (err)
+ return err;
break;

case HCIUARTGETPROTO:
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 4814ff0..1a97fa9 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -95,6 +95,7 @@ struct hci_uart {
/* HCI_UART proto flag bits */
#define HCI_UART_PROTO_SET 0
#define HCI_UART_REGISTERED 1
+#define HCI_UART_PROTO_BUSY 2

/* TX states */
#define HCI_UART_SENDING 1
--
1.9.1


2016-03-11 15:34:24

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH] Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data

Hi Loic,

> HCI_UART_PROTO_SET flag is set before hci_uart_set_proto call. If we
> receive data from tty layer during this procedure, proto pointer may
> not be assigned yet, leading to null pointer dereference in rx method
> hci_uart_tty_receive.
>
> This patch fixes this by setting the HCI_UART_PROTO_SET flag once proto
> is fully initialized and introduces HCI_UART_PROTO_BUSY flag to avoid
> concurrent call to set_proto. We could have fixed this with a mutex as
> well.
>
> Signed-off-by: Loic Poulain <[email protected]>
> ---
> drivers/bluetooth/hci_ldisc.c | 16 +++++++++-------
> drivers/bluetooth/hci_uart.h | 1 +
> 2 files changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index c00168a..fab2641 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -638,6 +638,7 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
> return err;
>
> hu->proto = p;
> + set_bit(HCI_UART_PROTO_SET, &hu->flags);
>
> err = hci_uart_register_dev(hu);
> if (err) {
> @@ -692,14 +693,15 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
>
> switch (cmd) {
> case HCIUARTSETPROTO:
> - if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
> - err = hci_uart_set_proto(hu, arg);
> - if (err) {
> - clear_bit(HCI_UART_PROTO_SET, &hu->flags);
> - return err;
> - }
> - } else
> + if (test_and_set_bit(HCI_UART_PROTO_BUSY, &hu->flags))
> return -EBUSY;
> + if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
> + err = -EBUSY;
> + else
> + err = hci_uart_set_proto(hu, arg);
> + clear_bit(HCI_UART_PROTO_BUSY, &hu->flags);
> + if (err)
> + return err;
> break;

I do not like overloading the PROTO_SET flag since that is only suppose to be used when the ioctl has been called. I think what we really want to introduce a PROTO_READY and set + check that later on.

Regards

Marcel