Skip to content

Commit

Permalink
appletalk: Fix skb allocation size in loopback case
Browse files Browse the repository at this point in the history
If a DDP broadcast packet is sent out to a non-gateway target, it is
also looped back. There is a potential for the loopback device to have a
longer hardware header length than the original target route's device,
which can result in the skb not being created with enough room for the
loopback device's hardware header. This patch fixes the issue by
determining that a loopback will be necessary prior to allocating the
skb, and if so, ensuring the skb has enough room.

This was discovered while testing a new driver that creates a LocalTalk
network interface (LTALK_HLEN = 1). It caused an skb_under_panic.

Signed-off-by: Doug Brown <doug@schmorgal.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
dougg3 authored and davem330 committed Feb 13, 2021
1 parent 0c9fc2e commit 39935dc
Showing 1 changed file with 21 additions and 12 deletions.
33 changes: 21 additions & 12 deletions net/appletalk/ddp.c
Expand Up @@ -1577,8 +1577,8 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
struct sk_buff *skb;
struct net_device *dev;
struct ddpehdr *ddp;
int size;
struct atalk_route *rt;
int size, hard_header_len;
struct atalk_route *rt, *rt_lo = NULL;
int err;

if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
Expand Down Expand Up @@ -1641,15 +1641,30 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n",
sk, size, dev->name);

size += dev->hard_header_len;
hard_header_len = dev->hard_header_len;
/* Leave room for loopback hardware header if necessary */
if (usat->sat_addr.s_node == ATADDR_BCAST &&
(dev->flags & IFF_LOOPBACK || !(rt->flags & RTF_GATEWAY))) {
struct atalk_addr at_lo;

at_lo.s_node = 0;
at_lo.s_net = 0;

rt_lo = atrtr_find(&at_lo);

if (rt_lo && rt_lo->dev->hard_header_len > hard_header_len)
hard_header_len = rt_lo->dev->hard_header_len;
}

size += hard_header_len;
release_sock(sk);
skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
lock_sock(sk);
if (!skb)
goto out;

skb_reserve(skb, ddp_dl->header_length);
skb_reserve(skb, dev->hard_header_len);
skb_reserve(skb, hard_header_len);
skb->dev = dev;

SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
Expand Down Expand Up @@ -1700,18 +1715,12 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
/* loop back */
skb_orphan(skb);
if (ddp->deh_dnode == ATADDR_BCAST) {
struct atalk_addr at_lo;

at_lo.s_node = 0;
at_lo.s_net = 0;

rt = atrtr_find(&at_lo);
if (!rt) {
if (!rt_lo) {
kfree_skb(skb);
err = -ENETUNREACH;
goto out;
}
dev = rt->dev;
dev = rt_lo->dev;
skb->dev = dev;
}
ddp_dl->request(ddp_dl, skb, dev->dev_addr);
Expand Down

0 comments on commit 39935dc

Please sign in to comment.