Skip to content

Commit 24f6be6

Browse files
committed
Don't apply the FD_SETSIZE check to Windows sockets
On Windows a SOCKET is an opaque handle, not a small index, and an fd_set is a counted array of sockets rather than a bitmask. libmodbus only ever puts a single socket in a set, so it can't overflow it. The fd >= FD_SETSIZE checks added in 7b13341 therefore don't apply on Windows, where they instead reject any socket whose handle value reaches FD_SETSIZE (64 by default) - which happens after a few dozen open handles and breaks Modbus TCP with EINVAL / "exceeds FD_SETSIZE". Wrap the check in a macro that keeps it on POSIX and disables it on Windows. POSIX behaviour is unchanged. Fixes #842
1 parent a9b025d commit 24f6be6

4 files changed

Lines changed: 24 additions & 9 deletions

File tree

src/modbus-private.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ typedef int ssize_t;
2222

2323
#include "modbus.h"
2424

25+
/* select()/FD_SET treat a descriptor as an index into a fixed-size bitmask on
26+
* POSIX, so a descriptor greater than or equal to FD_SETSIZE would write past
27+
* the end of the fd_set and must be rejected. On Windows a SOCKET is an opaque
28+
* handle whose numeric value is unrelated to FD_SETSIZE, and an fd_set is a
29+
* counted array of sockets (fd_count + fd_array[]) rather than a bitmask. Since
30+
* libmodbus only ever registers a single socket in a set, it can never overflow
31+
* fd_array; the POSIX range check therefore does not apply on Windows and, if
32+
* kept, wrongly rejects valid sockets whose handle value happens to reach
33+
* FD_SETSIZE (64 by default). Restrict the check to non-Windows platforms. */
34+
#if defined(_WIN32)
35+
#define MODBUS_FD_OUT_OF_RANGE(fd) (0)
36+
#else
37+
#define MODBUS_FD_OUT_OF_RANGE(fd) ((fd) >= FD_SETSIZE)
38+
#endif
39+
2540
MODBUS_BEGIN_DECLS
2641

2742
/* It's not really the minimal length (the real one is report slave ID

src/modbus-rtu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,7 @@ _modbus_rtu_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_t
12391239
}
12401240
/* Necessary after an error */
12411241
FD_ZERO(rset);
1242-
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
1242+
if (ctx->s < 0 || MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
12431243
errno = EINVAL;
12441244
return -1;
12451245
}

src/modbus-tcp.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ static int _connect(int sockfd,
305305

306306
/* Wait to be available in writing */
307307
FD_ZERO(&wset);
308-
if (sockfd >= FD_SETSIZE) {
308+
if (MODBUS_FD_OUT_OF_RANGE(sockfd)) {
309309
errno = EINVAL;
310310
return -1;
311311
}
@@ -363,7 +363,7 @@ static int _modbus_tcp_connect(modbus_t *ctx)
363363
return -1;
364364
}
365365

366-
if (s >= FD_SETSIZE) {
366+
if (MODBUS_FD_OUT_OF_RANGE(s)) {
367367
if (ctx->debug) {
368368
fprintf(
369369
stderr,
@@ -471,7 +471,7 @@ static int _modbus_tcp_pi_connect(modbus_t *ctx)
471471
if (s < 0)
472472
continue;
473473

474-
if (s >= FD_SETSIZE) {
474+
if (MODBUS_FD_OUT_OF_RANGE(s)) {
475475
if (ctx->debug) {
476476
fprintf(
477477
stderr,
@@ -550,7 +550,7 @@ static int _modbus_tcp_flush(modbus_t *ctx)
550550
tv.tv_sec = 0;
551551
tv.tv_usec = 0;
552552
FD_ZERO(&rset);
553-
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
553+
if (ctx->s < 0 || MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
554554
errno = EINVAL;
555555
return -1;
556556
}
@@ -805,7 +805,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *s)
805805
return -1;
806806
}
807807

808-
if (ctx->s >= FD_SETSIZE) {
808+
if (MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
809809
if (ctx->debug) {
810810
fprintf(
811811
stderr,
@@ -853,7 +853,7 @@ int modbus_tcp_pi_accept(modbus_t *ctx, int *s)
853853
return -1;
854854
}
855855

856-
if (ctx->s >= FD_SETSIZE) {
856+
if (MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
857857
if (ctx->debug) {
858858
fprintf(
859859
stderr,
@@ -890,7 +890,7 @@ _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_t
890890
}
891891
/* Necessary after an error */
892892
FD_ZERO(rset);
893-
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
893+
if (ctx->s < 0 || MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
894894
errno = EINVAL;
895895
return -1;
896896
}

src/modbus.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
392392

393393
/* Add a file descriptor to the set */
394394
FD_ZERO(&rset);
395-
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
395+
if (ctx->s < 0 || MODBUS_FD_OUT_OF_RANGE(ctx->s)) {
396396
if (ctx->debug) {
397397
fprintf(stderr, "ERROR Invalid socket descriptor %d\n", ctx->s);
398398
}

0 commit comments

Comments
 (0)