From c10a848cea89a8f0418fa0efec33c4e8507aab4b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 9 Feb 2014 20:59:10 -0500 Subject: [PATCH] Bluetooth: Verify dlci not in use before rfcomm_dev create Only one session/channel combination may be in use at any one time. However, the failure does not occur until the tty is opened (in rfcomm_dlc_open()). Because these settings are actually bound at rfcomm device creation (via RFCOMMCREATEDEV ioctl), validate and fail before creating the rfcomm tty device. Signed-off-by: Peter Hurley Tested-By: Alexander Holler Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 26 +++++++++++++++++++++++++- net/bluetooth/rfcomm/tty.c | 8 ++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 0d69936831fa..f8262a2783ec 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -241,6 +241,7 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); +struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); #define rfcomm_dlc_lock(d) spin_lock(&d->lock) #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ba115d472f7b..b378bbb6f8a7 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -360,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) return NULL; } +static int rfcomm_check_channel(u8 channel) +{ + return channel < 1 || channel > 30; +} + static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) { struct rfcomm_session *s; @@ -369,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d", d, d->state, src, dst, channel); - if (channel < 1 || channel > 30) + if (rfcomm_check_channel(channel)) return -EINVAL; if (d->state != BT_OPEN && d->state != BT_CLOSED) @@ -514,6 +519,25 @@ no_session: return r; } +struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel) +{ + struct rfcomm_session *s; + struct rfcomm_dlc *dlc = NULL; + u8 dlci; + + if (rfcomm_check_channel(channel)) + return ERR_PTR(-EINVAL); + + rfcomm_lock(); + s = rfcomm_session_get(src, dst); + if (s) { + dlci = __dlci(!s->initiator, channel); + dlc = rfcomm_dlc_get(s, dlci); + } + rfcomm_unlock(); + return dlc; +} + int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) { int len = skb->len; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 6ea08b05b53a..a58d693e1e61 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -385,6 +385,14 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg) dlc = rfcomm_pi(sk)->dlc; rfcomm_dlc_hold(dlc); } else { + /* Validate the channel is unused */ + dlc = rfcomm_dlc_exists(&req.src, &req.dst, req.channel); + if (IS_ERR(dlc)) + return PTR_ERR(dlc); + else if (dlc) { + rfcomm_dlc_put(dlc); + return -EBUSY; + } dlc = rfcomm_dlc_alloc(GFP_KERNEL); if (!dlc) return -ENOMEM; -- 2.20.1