Skip to content

Commit

Permalink
net: rework recvmsg handler msg_name and msg_namelen logic
Browse files Browse the repository at this point in the history
This patch now always passes msg->msg_namelen as 0. recvmsg handlers must
set msg_namelen to the proper size <= sizeof(struct sockaddr_storage)
to return msg_name to the user.

This prevents numerous uninitialized memory leaks we had in the
recvmsg handlers and makes it harder for new code to accidentally leak
uninitialized memory.

Optimize for the case recvfrom is called with NULL as address. We don't
need to copy the address at all, so set it to NULL before invoking the
recvmsg handler. We can do so, because all the recvmsg handlers must
cope with the case a plain read() is called on them. read() also sets
msg_name to NULL.

Also document these changes in include/linux/net.h as suggested by David
Miller.

Changes since RFC:

Set msg->msg_name = NULL if user specified a NULL in msg_name but had a
non-null msg_namelen in verify_iovec/verify_compat_iovec. This doesn't
affect sendto as it would bail out earlier while trying to copy-in the
address. It also more naturally reflects the logic by the callers of
verify_iovec.

With this change in place I could remove "
if (!uaddr || msg_sys->msg_namelen == 0)
	msg->msg_name = NULL
".

This change does not alter the user visible error logic as we ignore
msg_namelen as long as msg_name is NULL.

Also remove two unnecessary curly brackets in ___sys_recvmsg and change
comments to netdev style.

Cc: David Miller <[email protected]>
Suggested-by: Eric Dumazet <[email protected]>
Signed-off-by: Hannes Frederic Sowa <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
strssndktn authored and davem330 committed Nov 21, 2013
1 parent f873042 commit f3d3342
Show file tree
Hide file tree
Showing 35 changed files with 67 additions and 115 deletions.
2 changes: 0 additions & 2 deletions crypto/algif_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
else if (len < ds)
msg->msg_flags |= MSG_TRUNC;

msg->msg_namelen = 0;

lock_sock(sk);
if (ctx->more) {
ctx->more = 0;
Expand Down
1 change: 0 additions & 1 deletion crypto/algif_skcipher.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,6 @@ static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
long copied = 0;

lock_sock(sk);
msg->msg_namelen = 0;
for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
iovlen--, iov++) {
unsigned long seglen = iov->iov_len;
Expand Down
13 changes: 4 additions & 9 deletions drivers/isdn/mISDN/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
{
struct sk_buff *skb;
struct sock *sk = sock->sk;
struct sockaddr_mISDN *maddr;

int copied, err;

Expand All @@ -135,9 +134,9 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!skb)
return err;

if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
msg->msg_namelen = sizeof(struct sockaddr_mISDN);
maddr = (struct sockaddr_mISDN *)msg->msg_name;
if (msg->msg_name) {
struct sockaddr_mISDN *maddr = msg->msg_name;

maddr->family = AF_ISDN;
maddr->dev = _pms(sk)->dev->id;
if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
Expand All @@ -150,11 +149,7 @@ mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
maddr->sapi = _pms(sk)->ch.addr & 0xFF;
maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF;
}
} else {
if (msg->msg_namelen)
printk(KERN_WARNING "%s: too small namelen %d\n",
__func__, msg->msg_namelen);
msg->msg_namelen = 0;
msg->msg_namelen = sizeof(*maddr);
}

copied = skb->len + MISDN_HEADER_LEN;
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/ppp/pppoe.c
Original file line number Diff line number Diff line change
Expand Up @@ -979,8 +979,6 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
if (error < 0)
goto end;

m->msg_namelen = 0;

if (skb) {
total_len = min_t(size_t, total_len, skb->len);
error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
Expand Down
8 changes: 8 additions & 0 deletions include/linux/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ struct proto_ops {
#endif
int (*sendmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len);
/* Notes for implementing recvmsg:
* ===============================
* msg->msg_namelen should get updated by the recvmsg handlers
* iff msg_name != NULL. It is by default 0 to prevent
* returning uninitialized memory to user space. The recvfrom
* handlers can assume that msg.msg_name is either NULL or has
* a minimum size of sizeof(struct sockaddr_storage).
*/
int (*recvmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len,
int flags);
Expand Down
16 changes: 7 additions & 9 deletions net/appletalk/ddp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,6 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
size_t size, int flags)
{
struct sock *sk = sock->sk;
struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name;
struct ddpehdr *ddp;
int copied = 0;
int offset = 0;
Expand Down Expand Up @@ -1764,14 +1763,13 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
}
err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied);

if (!err) {
if (sat) {
sat->sat_family = AF_APPLETALK;
sat->sat_port = ddp->deh_sport;
sat->sat_addr.s_node = ddp->deh_snode;
sat->sat_addr.s_net = ddp->deh_snet;
}
msg->msg_namelen = sizeof(*sat);
if (!err && msg->msg_name) {
struct sockaddr_at *sat = msg->msg_name;
sat->sat_family = AF_APPLETALK;
sat->sat_port = ddp->deh_sport;
sat->sat_addr.s_node = ddp->deh_snode;
sat->sat_addr.s_net = ddp->deh_snet;
msg->msg_namelen = sizeof(*sat);
}

skb_free_datagram(sk, skb); /* Free the datagram. */
Expand Down
2 changes: 0 additions & 2 deletions net/atm/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,6 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
struct sk_buff *skb;
int copied, error = -EINVAL;

msg->msg_namelen = 0;

if (sock->state != SS_CONNECTED)
return -ENOTCONN;

Expand Down
4 changes: 2 additions & 2 deletions net/ax25/af_ax25.c
Original file line number Diff line number Diff line change
Expand Up @@ -1636,11 +1636,11 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock,

skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);

if (msg->msg_namelen != 0) {
struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
if (msg->msg_name) {
ax25_digi digi;
ax25_address src;
const unsigned char *mac = skb_mac_header(skb);
struct sockaddr_ax25 *sax = msg->msg_name;

memset(sax, 0, sizeof(struct full_sockaddr_ax25));
ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL,
Expand Down
9 changes: 2 additions & 7 deletions net/bluetooth/af_bluetooth.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,9 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,

skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) {
if (sk->sk_shutdown & RCV_SHUTDOWN) {
msg->msg_namelen = 0;
if (sk->sk_shutdown & RCV_SHUTDOWN)
return 0;
}

return err;
}

Expand All @@ -245,8 +244,6 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (bt_sk(sk)->skb_msg_name)
bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
&msg->msg_namelen);
else
msg->msg_namelen = 0;
}

skb_free_datagram(sk, skb);
Expand Down Expand Up @@ -295,8 +292,6 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags & MSG_OOB)
return -EOPNOTSUPP;

msg->msg_namelen = 0;

BT_DBG("sk %p size %zu", sk, size);

lock_sock(sk);
Expand Down
2 changes: 0 additions & 2 deletions net/bluetooth/hci_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -856,8 +856,6 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!skb)
return err;

msg->msg_namelen = 0;

copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
Expand Down
1 change: 0 additions & 1 deletion net/bluetooth/rfcomm/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,

if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
rfcomm_dlc_accept(d);
msg->msg_namelen = 0;
return 0;
}

Expand Down
1 change: 0 additions & 1 deletion net/bluetooth/sco.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,6 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
sco_conn_defer_accept(pi->conn->hcon, pi->setting);
sk->sk_state = BT_CONFIG;
msg->msg_namelen = 0;

release_sock(sk);
return 0;
Expand Down
4 changes: 0 additions & 4 deletions net/caif/caif_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,6 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
if (m->msg_flags&MSG_OOB)
goto read_error;

m->msg_namelen = 0;

skb = skb_recv_datagram(sk, flags, 0 , &ret);
if (!skb)
goto read_error;
Expand Down Expand Up @@ -361,8 +359,6 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags&MSG_OOB)
goto out;

msg->msg_namelen = 0;

/*
* Lock the socket to prevent queue disordering
* while sleeps in memcpy_tomsg
Expand Down
3 changes: 2 additions & 1 deletion net/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
if (err < 0)
return err;
}
kern_msg->msg_name = kern_address;
if (kern_msg->msg_name)
kern_msg->msg_name = kern_address;
} else
kern_msg->msg_name = NULL;

Expand Down
3 changes: 2 additions & 1 deletion net/core/iovec.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a
if (err < 0)
return err;
}
m->msg_name = address;
if (m->msg_name)
m->msg_name = address;
} else {
m->msg_name = NULL;
}
Expand Down
3 changes: 1 addition & 2 deletions net/ipx/af_ipx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1823,15 +1823,14 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
if (skb->tstamp.tv64)
sk->sk_stamp = skb->tstamp;

msg->msg_namelen = sizeof(*sipx);

if (sipx) {
sipx->sipx_family = AF_IPX;
sipx->sipx_port = ipx->ipx_source.sock;
memcpy(sipx->sipx_node, ipx->ipx_source.node, IPX_NODE_LEN);
sipx->sipx_network = IPX_SKB_CB(skb)->ipx_source_net;
sipx->sipx_type = ipx->ipx_type;
sipx->sipx_zero = 0;
msg->msg_namelen = sizeof(*sipx);
}
rc = copied;

Expand Down
4 changes: 0 additions & 4 deletions net/irda/af_irda.c
Original file line number Diff line number Diff line change
Expand Up @@ -1385,8 +1385,6 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,

IRDA_DEBUG(4, "%s()\n", __func__);

msg->msg_namelen = 0;

skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
if (!skb)
Expand Down Expand Up @@ -1451,8 +1449,6 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
timeo = sock_rcvtimeo(sk, noblock);

msg->msg_namelen = 0;

do {
int chunk;
struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
Expand Down
2 changes: 0 additions & 2 deletions net/iucv/af_iucv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1324,8 +1324,6 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
int err = 0;
u32 offset;

msg->msg_namelen = 0;

if ((sk->sk_state == IUCV_DISCONN) &&
skb_queue_empty(&iucv->backlog_skb_q) &&
skb_queue_empty(&sk->sk_receive_queue) &&
Expand Down
1 change: 0 additions & 1 deletion net/key/af_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -3616,7 +3616,6 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
goto out;

msg->msg_namelen = 0;
skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
if (skb == NULL)
goto out;
Expand Down
2 changes: 0 additions & 2 deletions net/l2tp/l2tp_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,6 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_state & PPPOX_BOUND)
goto end;

msg->msg_namelen = 0;

err = 0;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
Expand Down
2 changes: 0 additions & 2 deletions net/llc/af_llc.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,6 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
int target; /* Read at least this many bytes */
long timeo;

msg->msg_namelen = 0;

lock_sock(sk);
copied = -ENOTCONN;
if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
Expand Down
2 changes: 0 additions & 2 deletions net/netlink/af_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -2335,8 +2335,6 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
}
#endif

msg->msg_namelen = 0;

copied = data_skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
Expand Down
3 changes: 1 addition & 2 deletions net/netrom/af_netrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1179,10 +1179,9 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
sax->sax25_family = AF_NETROM;
skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call,
AX25_ADDR_LEN);
msg->msg_namelen = sizeof(*sax);
}

msg->msg_namelen = sizeof(*sax);

skb_free_datagram(sk, skb);

release_sock(sk);
Expand Down
2 changes: 0 additions & 2 deletions net/nfc/llcp_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,6 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,

pr_debug("%p %zu\n", sk, len);

msg->msg_namelen = 0;

lock_sock(sk);

if (sk->sk_state == LLCP_CLOSED &&
Expand Down
2 changes: 0 additions & 2 deletions net/nfc/rawsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!skb)
return rc;

msg->msg_namelen = 0;

copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
Expand Down
32 changes: 15 additions & 17 deletions net/packet/af_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -2660,7 +2660,6 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
struct sock *sk = sock->sk;
struct sk_buff *skb;
int copied, err;
struct sockaddr_ll *sll;
int vnet_hdr_len = 0;

err = -EINVAL;
Expand Down Expand Up @@ -2744,22 +2743,10 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
goto out_free;
}

/*
* If the address length field is there to be filled in, we fill
* it in now.
*/

sll = &PACKET_SKB_CB(skb)->sa.ll;
if (sock->type == SOCK_PACKET)
msg->msg_namelen = sizeof(struct sockaddr_pkt);
else
msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr);

/*
* You lose any data beyond the buffer you gave. If it worries a
* user program they can ask the device for its MTU anyway.
/* You lose any data beyond the buffer you gave. If it worries
* a user program they can ask the device for its MTU
* anyway.
*/

copied = skb->len;
if (copied > len) {
copied = len;
Expand All @@ -2772,9 +2759,20 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,

sock_recv_ts_and_drops(msg, sk, skb);

if (msg->msg_name)
if (msg->msg_name) {
/* If the address length field is there to be filled
* in, we fill it in now.
*/
if (sock->type == SOCK_PACKET) {
msg->msg_namelen = sizeof(struct sockaddr_pkt);
} else {
struct sockaddr_ll *sll = &PACKET_SKB_CB(skb)->sa.ll;
msg->msg_namelen = sll->sll_halen +
offsetof(struct sockaddr_ll, sll_addr);
}
memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
msg->msg_namelen);
}

if (pkt_sk(sk)->auxdata) {
struct tpacket_auxdata aux;
Expand Down
2 changes: 0 additions & 2 deletions net/rds/recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,6 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,

rdsdebug("size %zu flags 0x%x timeo %ld\n", size, msg_flags, timeo);

msg->msg_namelen = 0;

if (msg_flags & MSG_OOB)
goto out;

Expand Down
Loading

0 comments on commit f3d3342

Please sign in to comment.