Skip to content

Commit 7b13341

Browse files
committed
Fix FD_SET overflow when socket fd >= FD_SETSIZE
When a TCP server accepts a socket with a file descriptor >= FD_SETSIZE (commonly 1024), the FD_SET macro writes past the end of the fd_set buffer, causing memory corruption or abort via glibc __fdelt_chk. An attacker can trigger this by exhausting file descriptors so the next accepted connection gets a high-numbered fd, then sending any request to drive modbus_receive(). Guard all FD_SET call sites: reject fds >= FD_SETSIZE early at accept/connect time, and add defensive checks before each FD_SET use in _modbus_receive_msg, _connect, _modbus_tcp_select, _modbus_tcp_flush, and _modbus_rtu_select. Reported-by: Lukas Dresel <lukas@artiphishell.com> Reported-by: Fabio <team@artiphishell.com>
1 parent b7ee556 commit 7b13341

3 files changed

Lines changed: 77 additions & 0 deletions

File tree

src/modbus-rtu.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,10 @@ _modbus_rtu_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_t
11501150
}
11511151
/* Necessary after an error */
11521152
FD_ZERO(rset);
1153+
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
1154+
errno = EINVAL;
1155+
return -1;
1156+
}
11531157
FD_SET(ctx->s, rset);
11541158
} else {
11551159
return -1;

src/modbus-tcp.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ static int _connect(int sockfd,
297297

298298
/* Wait to be available in writing */
299299
FD_ZERO(&wset);
300+
if (sockfd >= FD_SETSIZE) {
301+
errno = EINVAL;
302+
return -1;
303+
}
300304
FD_SET(sockfd, &wset);
301305
rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
302306
if (rc < 0) {
@@ -350,6 +354,20 @@ static int _modbus_tcp_connect(modbus_t *ctx)
350354
return -1;
351355
}
352356

357+
if (ctx->s >= FD_SETSIZE) {
358+
if (ctx->debug) {
359+
fprintf(
360+
stderr,
361+
"ERROR Socket descriptor %d exceeds FD_SETSIZE (%d)\n",
362+
ctx->s,
363+
FD_SETSIZE);
364+
}
365+
close(ctx->s);
366+
ctx->s = -1;
367+
errno = EINVAL;
368+
return -1;
369+
}
370+
353371
rc = _modbus_tcp_set_ipv4_options(ctx->s);
354372
if (rc == -1) {
355373
close(ctx->s);
@@ -440,6 +458,18 @@ static int _modbus_tcp_pi_connect(modbus_t *ctx)
440458
if (s < 0)
441459
continue;
442460

461+
if (s >= FD_SETSIZE) {
462+
if (ctx->debug) {
463+
fprintf(
464+
stderr,
465+
"ERROR Socket descriptor %d exceeds FD_SETSIZE (%d)\n",
466+
s,
467+
FD_SETSIZE);
468+
}
469+
close(s);
470+
continue;
471+
}
472+
443473
if (ai_ptr->ai_family == AF_INET)
444474
_modbus_tcp_set_ipv4_options(s);
445475

@@ -501,6 +531,10 @@ static int _modbus_tcp_flush(modbus_t *ctx)
501531
tv.tv_sec = 0;
502532
tv.tv_usec = 0;
503533
FD_ZERO(&rset);
534+
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
535+
errno = EINVAL;
536+
return -1;
537+
}
504538
FD_SET(ctx->s, &rset);
505539
rc = select(ctx->s + 1, &rset, NULL, NULL, &tv);
506540
if (rc == -1) {
@@ -743,6 +777,20 @@ int modbus_tcp_accept(modbus_t *ctx, int *s)
743777
return -1;
744778
}
745779

780+
if (ctx->s >= FD_SETSIZE) {
781+
if (ctx->debug) {
782+
fprintf(
783+
stderr,
784+
"ERROR Socket descriptor %d exceeds FD_SETSIZE (%d)\n",
785+
ctx->s,
786+
FD_SETSIZE);
787+
}
788+
close(ctx->s);
789+
ctx->s = -1;
790+
errno = EINVAL;
791+
return -1;
792+
}
793+
746794
if (ctx->debug) {
747795
char buf[INET_ADDRSTRLEN];
748796
if (inet_ntop(AF_INET, &(addr.sin_addr), buf, INET_ADDRSTRLEN) == NULL) {
@@ -777,6 +825,20 @@ int modbus_tcp_pi_accept(modbus_t *ctx, int *s)
777825
return -1;
778826
}
779827

828+
if (ctx->s >= FD_SETSIZE) {
829+
if (ctx->debug) {
830+
fprintf(
831+
stderr,
832+
"ERROR Socket descriptor %d exceeds FD_SETSIZE (%d)\n",
833+
ctx->s,
834+
FD_SETSIZE);
835+
}
836+
close(ctx->s);
837+
ctx->s = -1;
838+
errno = EINVAL;
839+
return -1;
840+
}
841+
780842
if (ctx->debug) {
781843
char buf[INET6_ADDRSTRLEN];
782844
if (inet_ntop(AF_INET6, &(addr.sin6_addr), buf, INET6_ADDRSTRLEN) == NULL) {
@@ -800,6 +862,10 @@ _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_t
800862
}
801863
/* Necessary after an error */
802864
FD_ZERO(rset);
865+
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
866+
errno = EINVAL;
867+
return -1;
868+
}
803869
FD_SET(ctx->s, rset);
804870
} else {
805871
return -1;

src/modbus.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
385385

386386
/* Add a file descriptor to the set */
387387
FD_ZERO(&rset);
388+
if (ctx->s < 0 || ctx->s >= FD_SETSIZE) {
389+
if (ctx->debug) {
390+
fprintf(stderr, "ERROR Invalid socket descriptor %d\n", ctx->s);
391+
}
392+
errno = EINVAL;
393+
return -1;
394+
}
388395
FD_SET(ctx->s, &rset);
389396

390397
/* We need to analyse the message step by step. At the first step, we want

0 commit comments

Comments
 (0)