mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 13:41:48 -04:00
Bluetooth: Improve setsockopt() handling of malformed user input
The bt_copy_from_sockptr() return value is being misinterpreted by most users: a non-zero result is mistakenly assumed to represent an error code, but actually indicates the number of bytes that could not be copied. Remove bt_copy_from_sockptr() and adapt callers to use copy_safe_from_sockptr(). For sco_sock_setsockopt() (case BT_CODEC) use copy_struct_from_sockptr() to scrub parts of uninitialized buffer. Opportunistically, rename `len` to `optlen` in hci_sock_setsockopt_old() and hci_sock_setsockopt(). Fixes:51eda36d33("Bluetooth: SCO: Fix not validating setsockopt user input") Fixes:a97de7bff1("Bluetooth: RFCOMM: Fix not validating setsockopt user input") Fixes:4f3951242a("Bluetooth: L2CAP: Fix not validating setsockopt user input") Fixes:9e8742cdfc("Bluetooth: ISO: Fix not validating setsockopt user input") Fixes:b2186061d6("Bluetooth: hci_sock: Fix not validating setsockopt user input") Reviewed-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Reviewed-by: David Wei <dw@davidwei.uk> Signed-off-by: Michal Luczaj <mhal@rbox.co> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
committed by
Luiz Augusto von Dentz
parent
3dd002f200
commit
3e643e4efa
@@ -590,15 +590,6 @@ static inline struct sk_buff *bt_skb_sendmmsg(struct sock *sk,
|
|||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int bt_copy_from_sockptr(void *dst, size_t dst_size,
|
|
||||||
sockptr_t src, size_t src_size)
|
|
||||||
{
|
|
||||||
if (dst_size > src_size)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return copy_from_sockptr(dst, src, dst_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bt_to_errno(u16 code);
|
int bt_to_errno(u16 code);
|
||||||
__u8 bt_status(int err);
|
__u8 bt_status(int err);
|
||||||
|
|
||||||
|
|||||||
@@ -1926,7 +1926,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
||||||
sockptr_t optval, unsigned int len)
|
sockptr_t optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct hci_ufilter uf = { .opcode = 0 };
|
struct hci_ufilter uf = { .opcode = 0 };
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
@@ -1943,7 +1943,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case HCI_DATA_DIR:
|
case HCI_DATA_DIR:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1954,7 +1954,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_TIME_STAMP:
|
case HCI_TIME_STAMP:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1974,7 +1974,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
|||||||
uf.event_mask[1] = *((u32 *) f->event_mask + 1);
|
uf.event_mask[1] = *((u32 *) f->event_mask + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&uf, sizeof(uf), optval, len);
|
err = copy_safe_from_sockptr(&uf, sizeof(uf), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2005,7 +2005,7 @@ static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
|
static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
sockptr_t optval, unsigned int len)
|
sockptr_t optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@@ -2015,7 +2015,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
if (level == SOL_HCI)
|
if (level == SOL_HCI)
|
||||||
return hci_sock_setsockopt_old(sock, level, optname, optval,
|
return hci_sock_setsockopt_old(sock, level, optname, optval,
|
||||||
len);
|
optlen);
|
||||||
|
|
||||||
if (level != SOL_BLUETOOTH)
|
if (level != SOL_BLUETOOTH)
|
||||||
return -ENOPROTOOPT;
|
return -ENOPROTOOPT;
|
||||||
@@ -2035,7 +2035,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, len);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -1566,7 +1566,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1577,7 +1577,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_PKT_STATUS:
|
case BT_PKT_STATUS:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1596,7 +1596,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&qos, sizeof(qos), optval, optlen);
|
err = copy_safe_from_sockptr(&qos, sizeof(qos), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1617,8 +1617,8 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(iso_pi(sk)->base, optlen, optval,
|
err = copy_safe_from_sockptr(iso_pi(sk)->base, optlen, optval,
|
||||||
optlen);
|
optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -755,7 +755,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
|||||||
opts.max_tx = chan->max_tx;
|
opts.max_tx = chan->max_tx;
|
||||||
opts.txwin_size = chan->tx_win;
|
opts.txwin_size = chan->tx_win;
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opts, sizeof(opts), optval, optlen);
|
err = copy_safe_from_sockptr(&opts, sizeof(opts), optval,
|
||||||
|
optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -800,7 +801,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_LM:
|
case L2CAP_LM:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -909,7 +910,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
sec.level = BT_SECURITY_LOW;
|
sec.level = BT_SECURITY_LOW;
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
err = copy_safe_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -956,7 +957,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -970,7 +971,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_FLUSHABLE:
|
case BT_FLUSHABLE:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1004,7 +1005,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
pwr.force_active = BT_POWER_FORCE_ACTIVE_ON;
|
pwr.force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&pwr, sizeof(pwr), optval, optlen);
|
err = copy_safe_from_sockptr(&pwr, sizeof(pwr), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1015,7 +1016,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_CHANNEL_POLICY:
|
case BT_CHANNEL_POLICY:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1046,7 +1047,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&mtu, sizeof(mtu), optval, optlen);
|
err = copy_safe_from_sockptr(&mtu, sizeof(mtu), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1076,7 +1077,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&mode, sizeof(mode), optval, optlen);
|
err = copy_safe_from_sockptr(&mode, sizeof(mode), optval,
|
||||||
|
optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -629,10 +629,9 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname,
|
|||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case RFCOMM_LM:
|
case RFCOMM_LM:
|
||||||
if (bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen)) {
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
err = -EFAULT;
|
if (err)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (opt & RFCOMM_LM_FIPS) {
|
if (opt & RFCOMM_LM_FIPS) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@@ -685,7 +684,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
sec.level = BT_SECURITY_LOW;
|
sec.level = BT_SECURITY_LOW;
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
err = copy_safe_from_sockptr(&sec, sizeof(sec), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -703,7 +702,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -896,7 +896,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -915,8 +915,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
|
|
||||||
voice.setting = sco_pi(sk)->setting;
|
voice.setting = sco_pi(sk)->setting;
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(&voice, sizeof(voice), optval,
|
err = copy_safe_from_sockptr(&voice, sizeof(voice), optval,
|
||||||
optlen);
|
optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -941,7 +941,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_PKT_STATUS:
|
case BT_PKT_STATUS:
|
||||||
err = bt_copy_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -984,7 +984,8 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_copy_from_sockptr(buffer, optlen, optval, optlen);
|
err = copy_struct_from_sockptr(buffer, sizeof(buffer), optval,
|
||||||
|
optlen);
|
||||||
if (err) {
|
if (err) {
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user