sockets: prevent nested SYS_ARCH_PROTECT() for LWIP_NETCONN_FULLDUPLEX==1

This commit is contained in:
goldsimon 2017-11-19 14:37:29 +01:00
parent 8b6bb1a503
commit 2aed2fc215

View File

@ -338,19 +338,26 @@ sock_inc_used(struct lwip_sock *sock)
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
} }
/* Like sock_inc_used(), but called under SYS_ARCH_PROTECT lock. */
static void
sock_inc_used_locked(struct lwip_sock *sock)
{
LWIP_ASSERT("sock != NULL", sock != NULL);
++sock->fd_used;
LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
}
/* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being /* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
* released (and possibly reused) when used from more than one thread * released (and possibly reused) when used from more than one thread
* (e.g. read-while-write or close-while-write, etc) * (e.g. read-while-write or close-while-write, etc)
* This function is called at the end of functions using (try)get_socket*(). * This function is called at the end of functions using (try)get_socket*().
*/ */
static void static void
done_socket(struct lwip_sock *sock) done_socket_locked(struct lwip_sock *sock)
{ {
SYS_ARCH_DECL_PROTECT(lev);
LWIP_ASSERT("sock != NULL", sock != NULL); LWIP_ASSERT("sock != NULL", sock != NULL);
SYS_ARCH_PROTECT(lev);
LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0); LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
if (--sock->fd_used == 0) { if (--sock->fd_used == 0) {
if (sock->fd_free_pending) { if (sock->fd_free_pending) {
@ -359,11 +366,21 @@ done_socket(struct lwip_sock *sock)
free_socket(sock, sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP); free_socket(sock, sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP);
} }
} }
}
static void
done_socket(struct lwip_sock *sock)
{
SYS_ARCH_DECL_PROTECT(lev);
SYS_ARCH_PROTECT(lev);
done_socket_locked(sock);
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
} }
#else /* LWIP_NETCONN_FULLDUPLEX */ #else /* LWIP_NETCONN_FULLDUPLEX */
#define sock_inc_used(sock) #define sock_inc_used(sock)
#define sock_inc_used_locked(sock)
#define done_socket(sock) #define done_socket(sock)
#define done_socket_locked(sock)
#endif /* LWIP_NETCONN_FULLDUPLEX */ #endif /* LWIP_NETCONN_FULLDUPLEX */
/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */ /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
@ -395,6 +412,17 @@ tryget_socket_unconn(int fd)
return ret; return ret;
} }
/* Like tryget_socket_unconn(), but called under SYS_ARCH_PROTECT lock. */
static struct lwip_sock *
tryget_socket_unconn_locked(int fd)
{
struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
if (ret != NULL) {
sock_inc_used_locked(ret);
}
return ret;
}
/** /**
* Same as get_socket but doesn't set errno * Same as get_socket but doesn't set errno
* *
@ -1774,7 +1802,7 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
} }
/* First get the socket's status (protected)... */ /* First get the socket's status (protected)... */
SYS_ARCH_PROTECT(lev); SYS_ARCH_PROTECT(lev);
sock = tryget_socket_unconn(i); sock = tryget_socket_unconn_locked(i);
if (sock != NULL) { if (sock != NULL) {
void *lastdata = sock->lastdata.pbuf; void *lastdata = sock->lastdata.pbuf;
s16_t rcvevent = sock->rcvevent; s16_t rcvevent = sock->rcvevent;
@ -1833,7 +1861,7 @@ lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets
if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) { if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
struct lwip_sock *sock; struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev); SYS_ARCH_PROTECT(lev);
sock = tryget_socket_unconn(i); sock = tryget_socket_unconn_locked(i);
if (sock != NULL) { if (sock != NULL) {
/* leave the socket used until released by lwip_select_dec_sockets_used */ /* leave the socket used until released by lwip_select_dec_sockets_used */
FD_SET(i, used_sockets); FD_SET(i, used_sockets);
@ -1962,20 +1990,20 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
(exceptset && FD_ISSET(i, exceptset))) { (exceptset && FD_ISSET(i, exceptset))) {
struct lwip_sock *sock; struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev); SYS_ARCH_PROTECT(lev);
sock = tryget_socket_unconn(i); sock = tryget_socket_unconn_locked(i);
if (sock != NULL) { if (sock != NULL) {
sock->select_waiting++; sock->select_waiting++;
if (sock->select_waiting == 0) { if (sock->select_waiting == 0) {
/* overflow - too many threads waiting */ /* overflow - too many threads waiting */
sock->select_waiting--; sock->select_waiting--;
done_socket(sock); done_socket_locked(sock);
nready = -1; nready = -1;
maxfdp2 = i; maxfdp2 = i;
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
set_errno(EBUSY); set_errno(EBUSY);
break; break;
} }
done_socket(sock); done_socket_locked(sock);
} else { } else {
/* Not a valid socket */ /* Not a valid socket */
nready = -1; nready = -1;
@ -2021,14 +2049,14 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
(exceptset && FD_ISSET(i, exceptset))) { (exceptset && FD_ISSET(i, exceptset))) {
struct lwip_sock *sock; struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev); SYS_ARCH_PROTECT(lev);
sock = tryget_socket_unconn(i); sock = tryget_socket_unconn_locked(i);
if (sock != NULL) { if (sock != NULL) {
/* for now, handle select_waiting==0... */ /* for now, handle select_waiting==0... */
LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
if (sock->select_waiting > 0) { if (sock->select_waiting > 0) {
sock->select_waiting--; sock->select_waiting--;
} }
done_socket(sock); done_socket_locked(sock);
} else { } else {
/* Not a valid socket */ /* Not a valid socket */
nready = -1; nready = -1;
@ -2128,7 +2156,7 @@ lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) { if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
/* First get the socket's status (protected)... */ /* First get the socket's status (protected)... */
SYS_ARCH_PROTECT(lev); SYS_ARCH_PROTECT(lev);
sock = tryget_socket_unconn(fds[fdi].fd); sock = tryget_socket_unconn_locked(fds[fdi].fd);
if (sock != NULL) { if (sock != NULL) {
void* lastdata = sock->lastdata.pbuf; void* lastdata = sock->lastdata.pbuf;
s16_t rcvevent = sock->rcvevent; s16_t rcvevent = sock->rcvevent;
@ -2140,19 +2168,19 @@ lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
if (sock->select_waiting == 0) { if (sock->select_waiting == 0) {
/* overflow - too many threads waiting */ /* overflow - too many threads waiting */
sock->select_waiting--; sock->select_waiting--;
done_socket(sock); done_socket_locked(sock);
nready = -1; nready = -1;
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
break; break;
} }
done_socket(sock); done_socket_locked(sock);
} else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) { } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
/* for now, handle select_waiting==0... */ /* for now, handle select_waiting==0... */
LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
if (sock->select_waiting > 0) { if (sock->select_waiting > 0) {
sock->select_waiting--; sock->select_waiting--;
} }
done_socket(sock); done_socket_locked(sock);
} }
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
@ -2204,15 +2232,12 @@ static void
lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds) lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
{ {
nfds_t fdi; nfds_t fdi;
SYS_ARCH_DECL_PROTECT(lev);
if(fds) { if(fds) {
/* Go through each struct pollfd in the array. */ /* Go through each struct pollfd in the array. */
for (fdi = 0; fdi < nfds; fdi++) { for (fdi = 0; fdi < nfds; fdi++) {
SYS_ARCH_PROTECT(lev);
/* Increase the reference counter */ /* Increase the reference counter */
tryget_socket_unconn(fds[fdi].fd); tryget_socket_unconn(fds[fdi].fd);
SYS_ARCH_UNPROTECT(lev);
} }
} }
} }