Set sockets as non-blocking in tcp.c and add blocking checks

Changes:
- Added is_nonblocking flag to Descriptor structure in system.c
- Implemented mock_fcntl (Linux) and mock_ioctlsocket (Windows) to handle setting sockets as non-blocking
- Set all sockets as non-blocking in tcp.c:
  - Listen sockets in create_listen_socket()
  - Accepted sockets in tcp_translate_events()
  - Connecting sockets in tcp_connect()
- Added checks in mock_accept(), mock_recv(), and mock_send() to abort the simulation if a socket would block but is not configured as non-blocking
- This ensures proper non-blocking socket configuration and helps catch blocking socket errors during simulation
This commit is contained in:
Claude
2025-11-09 19:51:09 +00:00
parent bab8558511
commit 1a2c02203d
3 changed files with 156 additions and 0 deletions
+106
View File
@@ -4,6 +4,7 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "system.h"
#include "chunk_server.h"
@@ -98,6 +99,9 @@ typedef struct {
// last "poll" call.
void *context;
// Whether this socket is configured as non-blocking
bool is_nonblocking;
// Address bound to this descriptor by the
// "bind" call.
DescriptorAddress address;
@@ -811,6 +815,7 @@ SOCKET mock_socket(int domain, int type, int protocol)
desc->events = 0;
desc->revents = 0;
desc->context = NULL;
desc->is_nonblocking = false;
desc->address = (DescriptorAddress) { .type=DESC_ADDR_VOID };
current_process->num_desc++;
@@ -912,6 +917,11 @@ SOCKET mock_accept(SOCKET fd, void *addr, socklen_t *addr_len)
DescriptorHandle peer_handle;
if (!accept_queue_pop(&desc->accept_queue, &peer_handle)) {
// Socket would block - abort if not configured as non-blocking
if (!desc->is_nonblocking) {
fprintf(stderr, "SIMULATION ERROR: accept() would block on socket %d but socket is not configured as non-blocking\n", (int)fd);
abort();
}
SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK); // Would block (no pending connections)
return INVALID_SOCKET;
}
@@ -937,6 +947,7 @@ SOCKET mock_accept(SOCKET fd, void *addr, socklen_t *addr_len)
new_desc->events = 0;
new_desc->revents = 0;
new_desc->context = NULL;
new_desc->is_nonblocking = false;
new_desc->address = (DescriptorAddress) { .type=DESC_ADDR_VOID };
new_desc->connection_state = CONNECTION_ESTABLISHED;
new_desc->connection_peer = peer_handle;
@@ -1063,6 +1074,11 @@ int mock_recv(SOCKET fd, void *dst, int len, int flags)
// If no data available, would block
if (bytes_read == 0) {
// Socket would block - abort if not configured as non-blocking
if (!desc->is_nonblocking) {
fprintf(stderr, "SIMULATION ERROR: recv() would block on socket %d but socket is not configured as non-blocking\n", (int)fd);
abort();
}
SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK);
return -1;
}
@@ -1106,6 +1122,11 @@ int mock_send(SOCKET fd, void *src, int len, int flags)
// If queue is full, we would block
if (bytes_written < len) {
// Socket would block - abort if not configured as non-blocking
if (!desc->is_nonblocking && bytes_written == 0) {
fprintf(stderr, "SIMULATION ERROR: send() would block on socket %d but socket is not configured as non-blocking\n", (int)fd);
abort();
}
SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK);
return bytes_written > 0 ? bytes_written : -1;
}
@@ -1451,6 +1472,37 @@ BOOL mock_QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency)
return TRUE;
}
int mock_ioctlsocket(SOCKET fd, long cmd, u_long *argp)
{
if (fd == INVALID_SOCKET || (int)fd < 0 || (int)fd >= MAX_DESCRIPTORS) {
SET_SOCKET_ERROR(SOCKET_ERROR_BADF);
return -1;
}
int idx = (int) fd;
Descriptor *desc = &current_process->desc[idx];
if (desc->type == DESC_EMPTY) {
SET_SOCKET_ERROR(SOCKET_ERROR_BADF);
return -1;
}
if (desc->type != DESC_SOCKET &&
desc->type != DESC_LISTEN_SOCKET &&
desc->type != DESC_CONNECTION_SOCKET) {
SET_SOCKET_ERROR(SOCKET_ERROR_NOTSOCK);
return -1;
}
if (cmd == FIONBIO) {
desc->is_nonblocking = (*argp != 0);
return 0;
}
SET_SOCKET_ERROR(SOCKET_ERROR_INVAL);
return -1;
}
#else
int mock_clock_gettime(clockid_t clockid, struct timespec *tp)
@@ -1630,6 +1682,60 @@ int mock_mkdir(char *path, mode_t mode)
return mkdir(path, mode);
}
int mock_fcntl(int fd, int cmd, ...)
{
if (fd < 0 || fd >= MAX_DESCRIPTORS) {
errno = EBADF;
return -1;
}
int idx = fd;
Descriptor *desc = &current_process->desc[idx];
if (desc->type == DESC_EMPTY) {
errno = EBADF;
return -1;
}
if (cmd == F_GETFL) {
// Return flags - only O_NONBLOCK is tracked
if (desc->type == DESC_FILE) {
// For files, forward to real fcntl
return fcntl(desc->real_fd, cmd);
}
return desc->is_nonblocking ? O_NONBLOCK : 0;
}
if (cmd == F_SETFL) {
if (desc->type == DESC_FILE) {
// For files, forward to real fcntl
va_list args;
va_start(args, cmd);
int flags = va_arg(args, int);
va_end(args);
return fcntl(desc->real_fd, cmd, flags);
}
if (desc->type != DESC_SOCKET &&
desc->type != DESC_LISTEN_SOCKET &&
desc->type != DESC_CONNECTION_SOCKET) {
errno = EBADF;
return -1;
}
va_list args;
va_start(args, cmd);
int flags = va_arg(args, int);
va_end(args);
desc->is_nonblocking = (flags & O_NONBLOCK) != 0;
return 0;
}
errno = EINVAL;
return -1;
}
#endif // !_WIN32
#endif // BUILD_TEST
+6
View File
@@ -56,6 +56,7 @@ int mock_connect(SOCKET fd, void *addr, size_t addr_len);
#ifdef _WIN32
int mock_closesocket(SOCKET fd);
int mock_ioctlsocket(SOCKET fd, long cmd, u_long *argp);
HANDLE mock_CreateFileW(WCHAR *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
BOOL mock_CloseHandle(HANDLE handle);
BOOL mock_LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh);
@@ -80,6 +81,7 @@ int mock_fstat(int fd, struct stat *buf);
int mock_mkstemp(char *path);
char* mock_realpath(char *path, char *dst);
int mock_mkdir(char *path, mode_t mode);
int mock_fcntl(int fd, int cmd, ...);
#endif
// Common
@@ -100,6 +102,7 @@ int mock_mkdir(char *path, mode_t mode);
// Windows
#define sys__mkdir mock__mkdir
#define sys_closesocket mock_closesocket
#define sys_ioctlsocket mock_ioctlsocket
#define sys_CreateFileW mock_CreateFileW
#define sys_CloseHandle mock_CloseHandle
#define sys_LockFile mock_LockFile
@@ -124,6 +127,7 @@ int mock_mkdir(char *path, mode_t mode);
#define sys_mkstemp mock_mkstemp
#define sys_realpath mock_realpath
#define sys_clock_gettime mock_clock_gettime
#define sys_fcntl mock_fcntl
#define sys_getrandom mock_getrandom
#else
@@ -146,6 +150,7 @@ int mock_mkdir(char *path, mode_t mode);
// Windows
#define sys__mkdir _mkdir
#define sys_closesocket closesocket
#define sys_ioctlsocket ioctlsocket
#define sys_CreateFileW CreateFileW
#define sys_CloseHandle CloseHandle
#define sys_LockFile LockFile
@@ -170,6 +175,7 @@ int mock_mkdir(char *path, mode_t mode);
#define sys_mkstemp mkstemp
#define sys_realpath realpath
#define sys_clock_gettime clock_gettime
#define sys_fcntl fcntl
#endif
#endif // SYSTEM_INCLUDED
+44
View File
@@ -30,6 +30,21 @@ static SOCKET create_listen_socket(string addr, uint16_t port)
if (fd == INVALID_SOCKET)
return INVALID_SOCKET;
// Set socket as non-blocking
#ifdef _WIN32
u_long mode = 1;
if (sys_ioctlsocket(fd, FIONBIO, &mode) != 0) {
CLOSE_SOCKET(fd);
return INVALID_SOCKET;
}
#else
int flags = sys_fcntl(fd, F_GETFL, 0);
if (flags < 0 || sys_fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
CLOSE_SOCKET(fd);
return INVALID_SOCKET;
}
#endif
char tmp[1<<10];
if (addr.len >= (int) sizeof(tmp))
return INVALID_SOCKET;
@@ -200,6 +215,20 @@ int tcp_translate_events(TCP *tcp, Event *events, void **contexts, struct pollfd
SOCKET new_fd = sys_accept(tcp->listen_fd, NULL, NULL);
if (new_fd != INVALID_SOCKET) {
// Set accepted socket as non-blocking
#ifdef _WIN32
u_long mode = 1;
if (sys_ioctlsocket(new_fd, FIONBIO, &mode) != 0) {
CLOSE_SOCKET(new_fd);
continue;
}
#else
int flags = sys_fcntl(new_fd, F_GETFL, 0);
if (flags < 0 || sys_fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
CLOSE_SOCKET(new_fd);
continue;
}
#endif
events[num_events++] = (Event) { EVENT_CONNECT, tcp->num_conns };
conn_init(&tcp->conns[tcp->num_conns++], new_fd, false);
}
@@ -304,6 +333,21 @@ int tcp_connect(TCP *tcp, Address addr, int tag, ByteQueue **output)
if (fd == INVALID_SOCKET)
return -1;
// Set socket as non-blocking
#ifdef _WIN32
u_long mode = 1;
if (sys_ioctlsocket(fd, FIONBIO, &mode) != 0) {
CLOSE_SOCKET(fd);
return -1;
}
#else
int flags = sys_fcntl(fd, F_GETFL, 0);
if (flags < 0 || sys_fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
CLOSE_SOCKET(fd);
return -1;
}
#endif
int ret;
if (addr.is_ipv4) {
struct sockaddr_in buf;