* [PATCH net-next 1/7] Bluetooth: hci_sock: write the full optval for getsockopt
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 2/7] Bluetooth: hci_sock: convert to getsockopt_iter Breno Leitao
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
In hci_sock_getsockopt_old(), HCI_DATA_DIR and HCI_TIME_STAMP both store
their value into a local int and then call put_user(opt, optval). Because
optval is the function parameter typed char __user *, put_user sizes the
write from sizeof(*optval), so only the low byte of the int is copied to
userspace.
The matching setsockopt path reads sizeof(int) via copy_safe_from_sockptr,
so userspace passes a 4-byte buffer in both directions but previously got
back only one initialized byte on the read side.
Not sending this through 'net' tree given this bug is mostly invisble,
given opt is 0/1, and the last byte is being properly copied.
With this change, the upcoming translation to .getsockopt_iter becomes
mechanical.
FWIW: This behavior appeared in commit 1da177e4c3f4 ("Linux-2.6.12-rc2").
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/hci_sock.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 0290dea081f62..1823c06ba8940 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -2088,7 +2088,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, optval))
+ if (put_user(opt, (int __user *)optval))
err = -EFAULT;
break;
@@ -2098,7 +2098,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, optval))
+ if (put_user(opt, (int __user *)optval))
err = -EFAULT;
break;
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 2/7] Bluetooth: hci_sock: convert to getsockopt_iter
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 1/7] Bluetooth: hci_sock: write the full optval for getsockopt Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 3/7] Bluetooth: ISO: " Breno Leitao
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Convert HCI socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.
Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()
The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local int opt used by HCI_DATA_DIR and
HCI_TIME_STAMP.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/hci_sock.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 1823c06ba8940..61fec674a2078 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -27,6 +27,7 @@
#include <linux/export.h>
#include <linux/utsname.h>
#include <linux/sched.h>
+#include <linux/uio.h>
#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -2063,7 +2064,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
}
static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *sopt)
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
@@ -2071,8 +2072,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
BT_DBG("sk %p, opt %d", sk, optname);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = sopt->optlen;
lock_sock(sk);
@@ -2088,7 +2088,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, (int __user *)optval))
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -2098,7 +2099,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
else
opt = 0;
- if (put_user(opt, (int __user *)optval))
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -2114,7 +2116,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
}
len = min_t(unsigned int, len, sizeof(uf));
- if (copy_to_user(optval, &uf, len))
+ if (copy_to_iter(&uf, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -2129,16 +2131,16 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
}
static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *sopt)
{
struct sock *sk = sock->sk;
int err = 0;
+ u16 mtu;
BT_DBG("sk %p, opt %d", sk, optname);
if (level == SOL_HCI)
- return hci_sock_getsockopt_old(sock, level, optname, optval,
- optlen);
+ return hci_sock_getsockopt_old(sock, level, optname, sopt);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
@@ -2148,7 +2150,9 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case BT_SNDMTU:
case BT_RCVMTU:
- if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval))
+ mtu = hci_pi(sk)->mtu;
+ if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+ sizeof(mtu))
err = -EFAULT;
break;
@@ -2185,7 +2189,7 @@ static const struct proto_ops hci_sock_ops = {
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = hci_sock_setsockopt,
- .getsockopt = hci_sock_getsockopt,
+ .getsockopt_iter = hci_sock_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 3/7] Bluetooth: ISO: convert to getsockopt_iter
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 1/7] Bluetooth: hci_sock: write the full optval for getsockopt Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 2/7] Bluetooth: hci_sock: convert to getsockopt_iter Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 4/7] Bluetooth: RFCOMM: " Breno Leitao
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Convert ISO socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.
Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *opt
- Use opt->optlen for buffer length (input) and returned size (output)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/iso.c | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 7cb2864fe8724..0d792d36bb5ac 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -10,6 +10,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
+#include <linux/uio.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -1842,18 +1843,17 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
}
static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct sock *sk = sock->sk;
- int len, err = 0;
struct bt_iso_qos *qos;
+ int len, val, err = 0;
u8 base_len;
u8 *base;
BT_DBG("sk %p", sk);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = opt->optlen;
lock_sock(sk);
@@ -1864,15 +1864,17 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
- (u32 __user *)optval))
+ val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+ sizeof(val))
err = -EFAULT;
break;
case BT_PKT_STATUS:
- if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
- (int __user *)optval))
+ val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
+ if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+ sizeof(val))
err = -EFAULT;
break;
@@ -1880,7 +1882,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
qos = iso_sock_get_qos(sk);
len = min_t(unsigned int, len, sizeof(*qos));
- if (copy_to_user(optval, qos, len))
+ if (copy_to_iter(qos, len, &opt->iter_out) != len)
err = -EFAULT;
break;
@@ -1896,9 +1898,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
}
len = min_t(unsigned int, len, base_len);
- if (copy_to_user(optval, base, len))
- err = -EFAULT;
- if (put_user(len, optlen))
+ opt->optlen = len;
+ if (copy_to_iter(base, len, &opt->iter_out) != len)
err = -EFAULT;
break;
@@ -2660,7 +2661,7 @@ static const struct proto_ops iso_sock_ops = {
.socketpair = sock_no_socketpair,
.shutdown = iso_sock_shutdown,
.setsockopt = iso_sock_setsockopt,
- .getsockopt = iso_sock_getsockopt
+ .getsockopt_iter = iso_sock_getsockopt
};
static const struct net_proto_family iso_sock_family_ops = {
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 4/7] Bluetooth: RFCOMM: convert to getsockopt_iter
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
` (2 preceding siblings ...)
2026-05-11 10:41 ` [PATCH net-next 3/7] Bluetooth: ISO: " Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 5/7] Bluetooth: L2CAP: " Breno Leitao
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Convert RFCOMM socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.
Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()
The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local u32 opt used by RFCOMM_LM.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/rfcomm/sock.c | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index be6639cd6f590..9b22e4240ed14 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -28,6 +28,7 @@
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/sched/signal.h>
+#include <linux/uio.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -723,7 +724,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
return err;
}
-static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname,
+ sockopt_t *sopt)
{
struct sock *sk = sock->sk;
struct sock *l2cap_sk;
@@ -735,8 +737,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
BT_DBG("sk %p", sk);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = sopt->optlen;
lock_sock(sk);
@@ -765,7 +766,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
if (rfcomm_pi(sk)->role_switch)
opt |= RFCOMM_LM_MASTER;
- if (put_user(opt, (u32 __user *) optval))
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -785,7 +787,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
len = min(len, sizeof(cinfo));
- if (copy_to_user(optval, (char *) &cinfo, len))
+ if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -799,23 +801,24 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
return err;
}
-static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname,
+ sockopt_t *sopt)
{
struct sock *sk = sock->sk;
struct bt_security sec;
int err = 0;
size_t len;
+ u32 opt;
BT_DBG("sk %p", sk);
if (level == SOL_RFCOMM)
- return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
+ return rfcomm_sock_getsockopt_old(sock, optname, sopt);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
- return -EFAULT;
+ len = sopt->optlen;
lock_sock(sk);
@@ -830,7 +833,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
sec.key_size = 0;
len = min(len, sizeof(sec));
- if (copy_to_user(optval, (char *) &sec, len))
+ if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -841,8 +844,9 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
break;
}
- if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
- (u32 __user *) optval))
+ opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -1014,7 +1018,7 @@ static const struct proto_ops rfcomm_sock_ops = {
.recvmsg = rfcomm_sock_recvmsg,
.shutdown = rfcomm_sock_shutdown,
.setsockopt = rfcomm_sock_setsockopt,
- .getsockopt = rfcomm_sock_getsockopt,
+ .getsockopt_iter = rfcomm_sock_getsockopt,
.ioctl = rfcomm_sock_ioctl,
.gettstamp = sock_gettstamp,
.poll = bt_sock_poll,
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 5/7] Bluetooth: L2CAP: convert to getsockopt_iter
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
` (3 preceding siblings ...)
2026-05-11 10:41 ` [PATCH net-next 4/7] Bluetooth: RFCOMM: " Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion Breno Leitao
2026-05-11 10:41 ` [PATCH net-next 7/7] Bluetooth: SCO: convert to getsockopt_iter Breno Leitao
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Convert L2CAP socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.
Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()
The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local u32 opt used by L2CAP_LM. The same
naming is reused for the new u32 helper in l2cap_sock_getsockopt(),
with mtu and mval helpers covering the u16 and u8 cases.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/l2cap_sock.c | 61 ++++++++++++++++++++++++++++------------------
1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index cf590a67d3641..dede550d60319 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -31,6 +31,7 @@
#include <linux/export.h>
#include <linux/filter.h>
#include <linux/sched/signal.h>
+#include <linux/uio.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -433,7 +434,7 @@ static int l2cap_get_mode(struct l2cap_chan *chan)
}
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *sopt)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -445,8 +446,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
BT_DBG("sk %p", sk);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = sopt->optlen;
lock_sock(sk);
@@ -488,7 +488,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
BT_DBG("mode 0x%2.2x", chan->mode);
len = min(len, sizeof(opts));
- if (copy_to_user(optval, (char *) &opts, len))
+ if (copy_to_iter(&opts, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -520,7 +520,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
opt |= L2CAP_LM_RELIABLE;
- if (put_user(opt, (u32 __user *) optval))
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -538,7 +539,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
len = min(len, sizeof(cinfo));
- if (copy_to_user(optval, (char *) &cinfo, len))
+ if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -553,25 +554,26 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
}
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *sopt)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct bt_security sec;
struct bt_power pwr;
- u32 phys;
int len, mode, err = 0;
+ u32 opt;
+ u16 mtu;
+ u8 mval;
BT_DBG("sk %p", sk);
if (level == SOL_L2CAP)
- return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
+ return l2cap_sock_getsockopt_old(sock, optname, sopt);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
- return -EFAULT;
+ len = sopt->optlen;
lock_sock(sk);
@@ -595,7 +597,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
}
len = min_t(unsigned int, len, sizeof(sec));
- if (copy_to_user(optval, (char *) &sec, len))
+ if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
@@ -606,15 +608,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
- (u32 __user *) optval))
+ opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
case BT_FLUSHABLE:
- if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
- (u32 __user *) optval))
+ opt = test_bit(FLAG_FLUSHABLE, &chan->flags);
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -629,13 +633,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
len = min_t(unsigned int, len, sizeof(pwr));
- if (copy_to_user(optval, (char *) &pwr, len))
+ if (copy_to_iter(&pwr, len, &sopt->iter_out) != len)
err = -EFAULT;
break;
case BT_CHANNEL_POLICY:
- if (put_user(chan->chan_policy, (u32 __user *) optval))
+ opt = chan->chan_policy;
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -650,7 +656,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(chan->omtu, (u16 __user *) optval))
+ mtu = chan->omtu;
+ if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+ sizeof(mtu))
err = -EFAULT;
break;
@@ -660,7 +668,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(chan->imtu, (u16 __user *) optval))
+ mtu = chan->imtu;
+ if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+ sizeof(mtu))
err = -EFAULT;
break;
@@ -670,9 +680,10 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- phys = hci_conn_get_phy(chan->conn->hcon);
+ opt = hci_conn_get_phy(chan->conn->hcon);
- if (put_user(phys, (u32 __user *) optval))
+ if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+ sizeof(opt))
err = -EFAULT;
break;
@@ -693,7 +704,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(mode, (u8 __user *) optval))
+ mval = mode;
+ if (copy_to_iter(&mval, sizeof(mval), &sopt->iter_out) !=
+ sizeof(mval))
err = -EFAULT;
break;
@@ -2000,7 +2013,7 @@ static const struct proto_ops l2cap_sock_ops = {
.socketpair = sock_no_socketpair,
.shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt,
- .getsockopt = l2cap_sock_getsockopt
+ .getsockopt_iter = l2cap_sock_getsockopt
};
static const struct net_proto_family l2cap_sock_family_ops = {
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
` (4 preceding siblings ...)
2026-05-11 10:41 ` [PATCH net-next 5/7] Bluetooth: L2CAP: " Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
2026-05-11 13:47 ` Jakub Kicinski
2026-05-11 10:41 ` [PATCH net-next 7/7] Bluetooth: SCO: convert to getsockopt_iter Breno Leitao
6 siblings, 1 reply; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Add a single test for the SCO conversion that replaced an open-coded
ptr cursor in BT_CODEC with plain copy_to_iter() calls. The test
pre-fills the user buffer with a sentinel byte and:
- asserts the buffer is unchanged when the kernel returns -EBADFD
or -EOPNOTSUPP (the common case in CI, where there is no
controller exposing HCI_OFFLOAD_CODECS_ENABLED + a
get_data_path_id op), or
- asserts at least one byte changed when the call succeeds
(capable controller present), validating the converted
copy_to_iter() write path.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index 209569354d0e3..b3cac4be4af0d 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -11,6 +11,13 @@
* that always reports the required buffer length back via optlen,
* even when the user buffer is too small to receive any group bits.
* - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path.
+ * - bluetooth/sco: BT_CODEC exercises the SCO conversion that
+ * replaced an open-coded ptr cursor with copy_to_iter() — the
+ * test pre-fills the user buffer with a sentinel and asserts that
+ * the error-return paths (hit when no controller / no codec
+ * offload is available, the common case in CI) leave the buffer
+ * untouched. If a capable controller is present, the success
+ * path's copy_to_iter() write is validated instead.
*
* Author: Breno Leitao <leitao@debian.org>
*/
@@ -31,6 +38,15 @@
#define AF_VSOCK 40
#endif
+#ifndef AF_BLUETOOTH
+#define AF_BLUETOOTH 31
+#endif
+
+#define BTPROTO_SCO 2
+#define SOL_BLUETOOTH 274
+#define BT_CODEC 19
+#define BT_SENTINEL 0xa5
+
/* ---------- netlink ---------- */
FIXTURE(netlink)
@@ -297,4 +313,53 @@ TEST_F(vsock, connect_timeout_old_exact)
ASSERT_EQ(sizeof(tv), optlen);
}
+/* ---------- bluetooth ---------- */
+
+/* sco: BT_CODEC. The SCO conversion replaced an open-coded ptr
+ * cursor in this option with plain copy_to_iter() calls. Without a
+ * controller exposing HCI_OFFLOAD_CODECS_ENABLED + a get_data_path_id
+ * op, the kernel returns -EBADFD / -EOPNOTSUPP from an early check
+ * and must NOT touch the user buffer. With such a controller the
+ * call succeeds and the buffer is filled by copy_to_iter().
+ */
+TEST(bt_sco_codec)
+{
+ socklen_t optlen;
+ uint8_t buf[256];
+ int fd, ret;
+
+ fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (fd < 0)
+ SKIP(return, "AF_BLUETOOTH/SCO: %s", strerror(errno));
+
+ memset(buf, BT_SENTINEL, sizeof(buf));
+ optlen = sizeof(buf);
+ ret = getsockopt(fd, SOL_BLUETOOTH, BT_CODEC, buf, &optlen);
+
+ if (ret < 0) {
+ size_t i, changed = 0;
+
+ ASSERT_TRUE(errno == EBADFD || errno == EOPNOTSUPP)
+ TH_LOG("unexpected errno %d (%s)", errno,
+ strerror(errno));
+ for (i = 0; i < sizeof(buf); i++)
+ if (buf[i] != BT_SENTINEL)
+ changed++;
+ ASSERT_EQ(0, changed)
+ TH_LOG("error path modified %zu byte(s)", changed);
+ } else {
+ size_t i, changed = 0;
+
+ ASSERT_GT(optlen, 0);
+ ASSERT_LE(optlen, sizeof(buf));
+ for (i = 0; i < optlen; i++)
+ if (buf[i] != BT_SENTINEL)
+ changed++;
+ ASSERT_GT(changed, 0)
+ TH_LOG("success path left buffer untouched");
+ }
+
+ close(fd);
+}
+
TEST_HARNESS_MAIN
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
2026-05-11 10:41 ` [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion Breno Leitao
@ 2026-05-11 13:47 ` Jakub Kicinski
2026-05-11 14:19 ` Breno Leitao
0 siblings, 1 reply; 10+ messages in thread
From: Jakub Kicinski @ 2026-05-11 13:47 UTC (permalink / raw)
To: Breno Leitao
Cc: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, Shuah Khan,
linux-bluetooth, linux-kernel, netdev, linux-kselftest,
kernel-team
On Mon, 11 May 2026 03:41:51 -0700 Breno Leitao wrote:
> tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
please don't add Bluetooth tests under net/ (or at least they must
cleanly XFAIL if Bluetooth is not enabled). Also Bluetooth has its
own tree so not net-next in the subject.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
2026-05-11 13:47 ` Jakub Kicinski
@ 2026-05-11 14:19 ` Breno Leitao
0 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 14:19 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, Shuah Khan,
linux-bluetooth, linux-kernel, netdev, linux-kselftest,
kernel-team
On Mon, May 11, 2026 at 06:47:29AM -0700, Jakub Kicinski wrote:
> On Mon, 11 May 2026 03:41:51 -0700 Breno Leitao wrote:
> > tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
>
> please don't add Bluetooth tests under net/ (or at least they must
> cleanly XFAIL if Bluetooth is not enabled). Also Bluetooth has its
> own tree so not net-next in the subject.
Sorry about it. I will update.
Thanks,
--breno
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH net-next 7/7] Bluetooth: SCO: convert to getsockopt_iter
2026-05-11 10:41 [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter Breno Leitao
` (5 preceding siblings ...)
2026-05-11 10:41 ` [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion Breno Leitao
@ 2026-05-11 10:41 ` Breno Leitao
6 siblings, 0 replies; 10+ messages in thread
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan
Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
Breno Leitao, kernel-team
Convert SCO socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.
Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *opt
- Use opt->optlen for buffer length (input) and returned size (output)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Drop the open-coded ptr cursor in BT_CODEC; iter_out advances on
every copy_to_iter() naturally
- Add linux/uio.h for copy_to_iter()
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/bluetooth/sco.c | 59 ++++++++++++++++++++++++++++-------------------------
1 file changed, 31 insertions(+), 28 deletions(-)
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index eba44525d41d9..37dcba9edfab6 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -28,6 +28,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
+#include <linux/uio.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -1072,7 +1073,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
}
static int sco_sock_getsockopt_old(struct socket *sock, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct sock *sk = sock->sk;
struct sco_options opts;
@@ -1082,8 +1083,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
BT_DBG("sk %p", sk);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = opt->optlen;
lock_sock(sk);
@@ -1101,7 +1101,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
BT_DBG("mtu %u", opts.mtu);
len = min(len, sizeof(opts));
- if (copy_to_user(optval, (char *)&opts, len))
+ if (copy_to_iter(&opts, len, &opt->iter_out) != len)
err = -EFAULT;
break;
@@ -1119,7 +1119,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
len = min(len, sizeof(cinfo));
- if (copy_to_user(optval, (char *)&cinfo, len))
+ if (copy_to_iter(&cinfo, len, &opt->iter_out) != len)
err = -EFAULT;
break;
@@ -1134,15 +1134,15 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
}
static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct sock *sk = sock->sk;
- int len, err = 0;
+ int len, val, err = 0;
struct bt_voice voice;
u32 phys;
int buf_len;
struct codec_list *c;
- u8 num_codecs, i, __user *ptr;
+ u8 num_codecs, i;
struct hci_dev *hdev;
struct hci_codec_caps *caps;
struct bt_codec codec;
@@ -1150,10 +1150,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
BT_DBG("sk %p", sk);
if (level == SOL_SCO)
- return sco_sock_getsockopt_old(sock, optname, optval, optlen);
+ return sco_sock_getsockopt_old(sock, optname, opt);
- if (get_user(len, optlen))
- return -EFAULT;
+ len = opt->optlen;
lock_sock(sk);
@@ -1165,8 +1164,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
- (u32 __user *)optval))
+ val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+ sizeof(val))
err = -EFAULT;
break;
@@ -1175,7 +1175,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
voice.setting = sco_pi(sk)->setting;
len = min_t(unsigned int, len, sizeof(voice));
- if (copy_to_user(optval, (char *)&voice, len))
+ if (copy_to_iter(&voice, len, &opt->iter_out) != len)
err = -EFAULT;
break;
@@ -1188,13 +1188,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
phys = hci_conn_get_phy(sco_pi(sk)->conn->hcon);
- if (put_user(phys, (u32 __user *) optval))
+ if (copy_to_iter(&phys, sizeof(phys), &opt->iter_out) !=
+ sizeof(phys))
err = -EFAULT;
break;
case BT_PKT_STATUS:
- if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
- (int __user *)optval))
+ val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
+ if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+ sizeof(val))
err = -EFAULT;
break;
@@ -1205,7 +1207,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
break;
}
- if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval))
+ val = sco_pi(sk)->conn->mtu;
+ if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+ sizeof(val))
err = -EFAULT;
break;
@@ -1252,13 +1256,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
hci_dev_put(hdev);
return -ENOBUFS;
}
- ptr = optval;
- if (put_user(num_codecs, ptr)) {
+ if (copy_to_iter(&num_codecs, sizeof(num_codecs),
+ &opt->iter_out) != sizeof(num_codecs)) {
hci_dev_put(hdev);
return -EFAULT;
}
- ptr += sizeof(num_codecs);
/* Iterate all the codecs supported over SCO and populate
* codec data
@@ -1275,11 +1278,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
if (err < 0)
break;
codec.num_caps = c->num_caps;
- if (copy_to_user(ptr, &codec, sizeof(codec))) {
+ if (copy_to_iter(&codec, sizeof(codec), &opt->iter_out)
+ != sizeof(codec)) {
err = -EFAULT;
break;
}
- ptr += sizeof(codec);
/* find codec capabilities data length */
len = 0;
@@ -1289,11 +1292,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
}
/* copy codec capabilities data */
- if (len && copy_to_user(ptr, c->caps, len)) {
+ if (len &&
+ copy_to_iter(c->caps, len, &opt->iter_out) != len) {
err = -EFAULT;
break;
}
- ptr += len;
}
hci_dev_unlock(hdev);
@@ -1301,8 +1304,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
lock_sock(sk);
- if (!err && put_user(buf_len, optlen))
- err = -EFAULT;
+ if (!err)
+ opt->optlen = buf_len;
break;
@@ -1577,7 +1580,7 @@ static const struct proto_ops sco_sock_ops = {
.socketpair = sock_no_socketpair,
.shutdown = sco_sock_shutdown,
.setsockopt = sco_sock_setsockopt,
- .getsockopt = sco_sock_getsockopt
+ .getsockopt_iter = sco_sock_getsockopt
};
static const struct net_proto_family sco_sock_family_ops = {
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 10+ messages in thread