Skip to content

Commit d0efb16

Browse files
pccdavem330
authored andcommitted
net: don't unconditionally copy_from_user a struct ifreq for socket ioctls
A common implementation of isatty(3) involves calling a ioctl passing a dummy struct argument and checking whether the syscall failed -- bionic and glibc use TCGETS (passing a struct termios), and musl uses TIOCGWINSZ (passing a struct winsize). If the FD is a socket, we will copy sizeof(struct ifreq) bytes of data from the argument and return -EFAULT if that fails. The result is that the isatty implementations may return a non-POSIX-compliant value in errno in the case where part of the dummy struct argument is inaccessible, as both struct termios and struct winsize are smaller than struct ifreq (at least on arm64). Although there is usually enough stack space following the argument on the stack that this did not present a practical problem up to now, with MTE stack instrumentation it's more likely for the copy to fail, as the memory following the struct may have a different tag. Fix the problem by adding an early check for whether the ioctl is a valid socket ioctl, and return -ENOTTY if it isn't. Fixes: 44c02a2 ("dev_ioctl(): move copyin/copyout to callers") Link: https://linux-review.googlesource.com/id/I869da6cf6daabc3e4b7b82ac979683ba05e27d4d Signed-off-by: Peter Collingbourne <[email protected]> Cc: <[email protected]> # 4.19 Signed-off-by: David S. Miller <[email protected]>
1 parent 73367f0 commit d0efb16

File tree

2 files changed

+9
-1
lines changed

2 files changed

+9
-1
lines changed

include/linux/netdevice.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4012,6 +4012,10 @@ int netdev_rx_handler_register(struct net_device *dev,
40124012
void netdev_rx_handler_unregister(struct net_device *dev);
40134013

40144014
bool dev_valid_name(const char *name);
4015+
static inline bool is_socket_ioctl_cmd(unsigned int cmd)
4016+
{
4017+
return _IOC_TYPE(cmd) == SOCK_IOC_TYPE;
4018+
}
40154019
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
40164020
bool *need_copyout);
40174021
int dev_ifconf(struct net *net, struct ifconf *, int);

net/socket.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,7 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
11091109
rtnl_unlock();
11101110
if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
11111111
err = -EFAULT;
1112-
} else {
1112+
} else if (is_socket_ioctl_cmd(cmd)) {
11131113
struct ifreq ifr;
11141114
bool need_copyout;
11151115
if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
@@ -1118,6 +1118,8 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
11181118
if (!err && need_copyout)
11191119
if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
11201120
return -EFAULT;
1121+
} else {
1122+
err = -ENOTTY;
11211123
}
11221124
return err;
11231125
}
@@ -3306,6 +3308,8 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
33063308
struct ifreq ifreq;
33073309
u32 data32;
33083310

3311+
if (!is_socket_ioctl_cmd(cmd))
3312+
return -ENOTTY;
33093313
if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ))
33103314
return -EFAULT;
33113315
if (get_user(data32, &u_ifreq32->ifr_data))

0 commit comments

Comments
 (0)