mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-25 10:23:23 +00:00
Fix panic when listen backlog is one
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
f6c230f756
commit
040f5a53ae
140
regression/apps/network/listen_backlog.c
Normal file
140
regression/apps/network/listen_backlog.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int new_bound_socket(struct sockaddr_in *addr)
|
||||||
|
{
|
||||||
|
int sockfd;
|
||||||
|
|
||||||
|
sockfd = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
perror("new_bound_socket: socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
|
||||||
|
perror("new_bound_socket: bind");
|
||||||
|
close(sockfd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int new_connected_socket(struct sockaddr_in *addr)
|
||||||
|
{
|
||||||
|
int sockfd;
|
||||||
|
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
perror("new_connected_socket: socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sockfd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
|
||||||
|
perror("new_connected_socket: connect");
|
||||||
|
close(sockfd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_TEST_BACKLOG 5
|
||||||
|
|
||||||
|
int test_listen_backlog(struct sockaddr_in *addr, int backlog)
|
||||||
|
{
|
||||||
|
int listenfd;
|
||||||
|
int connectfd[MAX_TEST_BACKLOG], acceptfd[MAX_TEST_BACKLOG];
|
||||||
|
int num_connectfd = 0, num_acceptfd = 0;
|
||||||
|
int ret = -1;
|
||||||
|
struct sockaddr caddr;
|
||||||
|
socklen_t caddrlen = sizeof(caddr);
|
||||||
|
|
||||||
|
listenfd = new_bound_socket(addr);
|
||||||
|
if (listenfd < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test failed: Error occurs in new_bound_socket\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(listenfd, backlog) < 0) {
|
||||||
|
perror("listen");
|
||||||
|
fprintf(stderr, "Test failed: Error occurs in listen\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; num_connectfd < backlog; ++num_connectfd) {
|
||||||
|
connectfd[num_connectfd] = new_connected_socket(addr);
|
||||||
|
if (connectfd[num_connectfd] < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_connectfd != backlog) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test failed: listen(backlog=%d) allows only %d pending connections\n",
|
||||||
|
backlog, num_connectfd);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test passed: listen(backlog=%d) allows >=%d pending connections\n",
|
||||||
|
backlog, num_connectfd);
|
||||||
|
|
||||||
|
for (; num_acceptfd < num_connectfd; ++num_acceptfd) {
|
||||||
|
acceptfd[num_acceptfd] = accept(listenfd, &caddr, &caddrlen);
|
||||||
|
if (acceptfd[num_acceptfd] < 0) {
|
||||||
|
perror("accept");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_acceptfd != num_connectfd) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test failed: Only %d pending connections can be accept()'ed out "
|
||||||
|
"of %d\n",
|
||||||
|
num_acceptfd, num_connectfd);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test passed: All of %d pending connections can be accept()'ed\n",
|
||||||
|
num_acceptfd);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
while (--num_acceptfd >= 0)
|
||||||
|
close(acceptfd[num_acceptfd]);
|
||||||
|
|
||||||
|
while (--num_connectfd >= 0)
|
||||||
|
close(connectfd[num_connectfd]);
|
||||||
|
|
||||||
|
close(listenfd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int backlog;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
if (inet_aton("127.0.0.1", &addr.sin_addr) < 0) {
|
||||||
|
fprintf(stderr, "inet_aton cannot parse 127.0.0.1\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (backlog = 0; backlog <= MAX_TEST_BACKLOG; ++backlog) {
|
||||||
|
// Avoid "bind: Address already in use"
|
||||||
|
addr.sin_port = htons(8080 + backlog);
|
||||||
|
|
||||||
|
err = test_listen_backlog(&addr, backlog);
|
||||||
|
if (err != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err ? -1 : 0;
|
||||||
|
}
|
@ -12,7 +12,9 @@ use super::connected::ConnectedStream;
|
|||||||
pub struct ListenStream {
|
pub struct ListenStream {
|
||||||
is_nonblocking: AtomicBool,
|
is_nonblocking: AtomicBool,
|
||||||
backlog: usize,
|
backlog: usize,
|
||||||
/// Sockets also listening at LocalEndPoint when called `listen`
|
/// A bound socket held to ensure the TCP port cannot be released
|
||||||
|
bound_socket: Arc<AnyBoundSocket>,
|
||||||
|
/// Backlog sockets listening at the local endpoint
|
||||||
backlog_sockets: RwLock<Vec<BacklogSocket>>,
|
backlog_sockets: RwLock<Vec<BacklogSocket>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,12 +24,11 @@ impl ListenStream {
|
|||||||
bound_socket: Arc<AnyBoundSocket>,
|
bound_socket: Arc<AnyBoundSocket>,
|
||||||
backlog: usize,
|
backlog: usize,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
debug_assert!(backlog >= 1);
|
|
||||||
let backlog_socket = BacklogSocket::new(&bound_socket)?;
|
|
||||||
let listen_stream = Self {
|
let listen_stream = Self {
|
||||||
is_nonblocking: AtomicBool::new(nonblocking),
|
is_nonblocking: AtomicBool::new(nonblocking),
|
||||||
backlog,
|
backlog,
|
||||||
backlog_sockets: RwLock::new(vec![backlog_socket]),
|
bound_socket,
|
||||||
|
backlog_sockets: RwLock::new(Vec::new()),
|
||||||
};
|
};
|
||||||
listen_stream.fill_backlog_sockets()?;
|
listen_stream.fill_backlog_sockets()?;
|
||||||
Ok(listen_stream)
|
Ok(listen_stream)
|
||||||
@ -71,9 +72,8 @@ impl ListenStream {
|
|||||||
if backlog == current_backlog_len {
|
if backlog == current_backlog_len {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let bound_socket = backlog_sockets[0].bound_socket.clone();
|
|
||||||
for _ in current_backlog_len..backlog {
|
for _ in current_backlog_len..backlog {
|
||||||
let backlog_socket = BacklogSocket::new(&bound_socket)?;
|
let backlog_socket = BacklogSocket::new(&self.bound_socket)?;
|
||||||
backlog_sockets.push(backlog_socket);
|
backlog_sockets.push(backlog_socket);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -128,7 +128,7 @@ struct BacklogSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BacklogSocket {
|
impl BacklogSocket {
|
||||||
fn new(bound_socket: &AnyBoundSocket) -> Result<Self> {
|
fn new(bound_socket: &Arc<AnyBoundSocket>) -> Result<Self> {
|
||||||
let local_endpoint = bound_socket.local_endpoint().ok_or(Error::with_message(
|
let local_endpoint = bound_socket.local_endpoint().ok_or(Error::with_message(
|
||||||
Errno::EINVAL,
|
Errno::EINVAL,
|
||||||
"the socket is not bound",
|
"the socket is not bound",
|
||||||
|
Reference in New Issue
Block a user