net/icmp: fix race condition in icmp recvmsg

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2021-05-20 18:16:59 +08:00 committed by Xiang Xiao
parent ae613446c8
commit 39245f63fe
2 changed files with 175 additions and 170 deletions

View File

@ -378,6 +378,8 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
}
}
net_lock();
/* We cannot receive a response from a device until a request has been
* sent to the devivce.
*/
@ -389,39 +391,6 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
/* Check if there is buffered read-ahead data for this socket. We may have
* already received the response to previous command.
*/
if (!IOB_QEMPTY(&conn->readahead))
{
return icmp_readahead(conn, buf, len,
(FAR struct sockaddr_in *)from, fromlen);
}
/* Handle non-blocking ICMP sockets */
if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
return -EAGAIN;
}
/* Initialize the state structure */
memset(&state, 0, sizeof(struct icmp_recvfrom_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.recv_sem, 0, 0);
nxsem_set_protocol(&state.recv_sem, SEM_PRIO_NONE);
state.recv_sock = psock; /* The IPPROTO_ICMP socket instance */
state.recv_result = -ENOMEM; /* Assume allocation failure */
state.recv_buf = buf; /* Location to return the response */
state.recv_buflen = len; /* Size of the response */
/* Get the device that was used to send the ICMP request. */
dev = conn->dev;
@ -432,68 +401,102 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
net_lock();
/* Check if there is buffered read-ahead data for this socket. We may have
* already received the response to previous command.
*/
/* Set up the callback */
state.recv_cb = icmp_callback_alloc(dev, conn);
if (state.recv_cb != NULL)
if (!IOB_QEMPTY(&conn->readahead))
{
state.recv_cb->flags = (ICMP_NEWDATA | NETDEV_DOWN);
state.recv_cb->priv = (FAR void *)&state;
state.recv_cb->event = recvfrom_eventhandler;
ret = icmp_readahead(conn, buf, len,
(FAR struct sockaddr_in *)from, fromlen);
}
else if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
/* Handle non-blocking ICMP sockets */
/* Wait for either the response to be received or for timeout to
* occur. net_timedwait will also terminate if a signal is received.
ret = -EAGAIN;
}
else
{
/* Initialize the state structure */
memset(&state, 0, sizeof(struct icmp_recvfrom_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
ret = net_timedwait(&state.recv_sem, _SO_TIMEOUT(psock->s_rcvtimeo));
if (ret < 0)
nxsem_init(&state.recv_sem, 0, 0);
nxsem_set_protocol(&state.recv_sem, SEM_PRIO_NONE);
state.recv_sock = psock; /* The IPPROTO_ICMP socket instance */
state.recv_result = -ENOMEM; /* Assume allocation failure */
state.recv_buf = buf; /* Location to return the response */
state.recv_buflen = len; /* Size of the response */
/* Set up the callback */
state.recv_cb = icmp_callback_alloc(dev, conn);
if (state.recv_cb != NULL)
{
state.recv_result = ret;
state.recv_cb->flags = (ICMP_NEWDATA | NETDEV_DOWN);
state.recv_cb->priv = (FAR void *)&state;
state.recv_cb->event = recvfrom_eventhandler;
/* Wait for either the response to be received or for timeout to
* occur. net_timedwait will also terminate if a signal is
* received.
*/
ret = net_timedwait(&state.recv_sem,
_SO_TIMEOUT(psock->s_rcvtimeo));
if (ret < 0)
{
state.recv_result = ret;
}
icmp_callback_free(dev, conn, state.recv_cb);
}
icmp_callback_free(dev, conn, state.recv_cb);
/* Return the negated error number in the event of a failure, or the
* number of bytes received on success.
*/
if (state.recv_result < 0)
{
nerr("ERROR: Return error=%d\n", state.recv_result);
ret = state.recv_result;
goto errout;
}
if (from != NULL)
{
inaddr = (FAR struct sockaddr_in *)from;
inaddr->sin_family = AF_INET;
inaddr->sin_port = 0;
net_ipv4addr_copy(inaddr->sin_addr.s_addr, state.recv_from);
}
ret = state.recv_result;
/* If there a no further outstanding requests,
* make sure that the request struct is left pristine.
*/
errout:
if (conn->nreqs < 1)
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead, IOBUSER_NET_SOCK_ICMP);
}
}
net_unlock();
/* Return the negated error number in the event of a failure, or the
* number of bytes received on success.
*/
if (state.recv_result < 0)
{
nerr("ERROR: Return error=%d\n", state.recv_result);
ret = state.recv_result;
goto errout;
}
if (from != NULL)
{
inaddr = (FAR struct sockaddr_in *)from;
inaddr->sin_family = AF_INET;
inaddr->sin_port = 0;
net_ipv4addr_copy(inaddr->sin_addr.s_addr, state.recv_from);
}
ret = state.recv_result;
/* If there a no further outstanding requests, make sure that the request
* struct is left pristine.
*/
errout:
if (conn->nreqs < 1)
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead, IOBUSER_NET_SOCK_ICMP);
}
return ret;
}

View File

@ -385,6 +385,8 @@ ssize_t icmpv6_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
}
}
net_lock();
/* We cannot receive a response from a device until a request has been
* sent to the devivce.
*/
@ -396,39 +398,6 @@ ssize_t icmpv6_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
/* Check if there is buffered read-ahead data for this socket. We may have
* already received the response to previous command.
*/
if (!IOB_QEMPTY(&conn->readahead))
{
return icmpv6_readahead(conn, buf, len,
(FAR struct sockaddr_in6 *)from, fromlen);
}
/* Handle non-blocking ICMP sockets */
if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
return -EAGAIN;
}
/* Initialize the state structure */
memset(&state, 0, sizeof(struct icmpv6_recvfrom_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.recv_sem, 0, 0);
nxsem_set_protocol(&state.recv_sem, SEM_PRIO_NONE);
state.recv_sock = psock; /* The IPPROTO_ICMP6 socket instance */
state.recv_result = -ENOMEM; /* Assume allocation failure */
state.recv_buf = buf; /* Location to return the response */
state.recv_buflen = len; /* Size of the response */
/* Get the device that was used to send the ICMPv6 request. */
dev = conn->dev;
@ -439,72 +408,105 @@ ssize_t icmpv6_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
goto errout;
}
net_lock();
/* Check if there is buffered read-ahead data for this socket. We may have
* already received the response to previous command.
*/
/* Set up the callback */
state.recv_cb = icmpv6_callback_alloc(dev, conn);
if (state.recv_cb)
if (!IOB_QEMPTY(&conn->readahead))
{
state.recv_cb->flags = (ICMPv6_NEWDATA | NETDEV_DOWN);
state.recv_cb->priv = (FAR void *)&state;
state.recv_cb->event = recvfrom_eventhandler;
ret = icmpv6_readahead(conn, buf, len,
(FAR struct sockaddr_in6 *)from, fromlen);
}
else if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
/* Handle non-blocking ICMP sockets */
/* Wait for either the response to be received or for timeout to
* occur. (1) net_timedwait will also terminate if a signal is
* received, (2) interrupts may be disabled! They will be re-enabled
* while the task sleeps and automatically re-enabled when the task
* restarts.
ret = -EAGAIN;
}
else
{
/* Initialize the state structure */
memset(&state, 0, sizeof(struct icmpv6_recvfrom_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
ret = net_timedwait(&state.recv_sem, _SO_TIMEOUT(psock->s_rcvtimeo));
if (ret < 0)
nxsem_init(&state.recv_sem, 0, 0);
nxsem_set_protocol(&state.recv_sem, SEM_PRIO_NONE);
state.recv_sock = psock; /* The IPPROTO_ICMP6 socket instance */
state.recv_result = -ENOMEM; /* Assume allocation failure */
state.recv_buf = buf; /* Location to return the response */
state.recv_buflen = len; /* Size of the response */
/* Set up the callback */
state.recv_cb = icmpv6_callback_alloc(dev, conn);
if (state.recv_cb)
{
state.recv_result = ret;
state.recv_cb->flags = (ICMPv6_NEWDATA | NETDEV_DOWN);
state.recv_cb->priv = (FAR void *)&state;
state.recv_cb->event = recvfrom_eventhandler;
/* Wait for either the response to be received or for timeout to
* occur. (1) net_timedwait will also terminate if a signal is
* received, (2) interrupts may be disabled! They will be
* re-enabled while the task sleeps and automatically re-enabled
* when the task restarts.
*/
ret = net_timedwait(&state.recv_sem,
_SO_TIMEOUT(psock->s_rcvtimeo));
if (ret < 0)
{
state.recv_result = ret;
}
icmpv6_callback_free(dev, conn, state.recv_cb);
}
icmpv6_callback_free(dev, conn, state.recv_cb);
/* Return the negated error number in the event of a failure, or the
* number of bytes received on success.
*/
if (state.recv_result < 0)
{
nerr("ERROR: Return error=%d\n", state.recv_result);
ret = state.recv_result;
goto errout;
}
if (from != NULL)
{
inaddr = (FAR struct sockaddr_in6 *)from;
inaddr->sin6_family = AF_INET6;
inaddr->sin6_port = 0;
net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16,
state.recv_from.s6_addr16);
}
ret = state.recv_result;
/* If there a no further outstanding requests,
* make sure that the request struct is left pristine.
*/
errout:
if (conn->nreqs < 1)
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead, IOBUSER_NET_SOCK_ICMPv6);
}
}
net_unlock();
/* Return the negated error number in the event of a failure, or the
* number of bytes received on success.
*/
if (state.recv_result < 0)
{
nerr("ERROR: Return error=%d\n", state.recv_result);
ret = state.recv_result;
goto errout;
}
if (from != NULL)
{
inaddr = (FAR struct sockaddr_in6 *)from;
inaddr->sin6_family = AF_INET6;
inaddr->sin6_port = 0;
net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16,
state.recv_from.s6_addr16);
}
ret = state.recv_result;
/* If there a no further outstanding requests, make sure that the request
* struct is left pristine.
*/
errout:
if (conn->nreqs < 1)
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead, IOBUSER_NET_SOCK_ICMPv6);
}
return ret;
}