library: replace atol with bounded strtoll for RESP length parsing

atol returns undefined behavior on overflow per C11 7.22.1.4. glibc
saturates to LONG_MAX, but musl, BSD libc, and Windows libc differ.

Replace atol / atoi at the three RESP length parse sites in library.c
with strtoll plus ERANGE rejection. The wire input is server-
controlled; an out-of-range value should drop the reply rather than
land an implementation-defined value in downstream length arithmetic.
This commit is contained in:
Ilia Alshanetsky
2026-05-19 15:34:14 -04:00
committed by Michael Grunder
parent b112875b70
commit 5c6e2d2b3c
+33 -3
View File
@@ -7,6 +7,8 @@
#include "common.h"
#include "php_network.h"
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#ifdef HAVE_REDIS_IGBINARY
#include "igbinary/igbinary.h"
@@ -364,7 +366,17 @@ read_mbulk_header(RedisSock *redis_sock, int *nelem)
return FAILURE;
}
*nelem = atoi(line + 1);
{
char *endptr;
long long n;
errno = 0;
n = strtoll(line + 1, &endptr, 10);
if (endptr == line + 1 || errno == ERANGE || n < -1 || n > INT_MAX) {
return FAILURE;
}
*nelem = (int)n;
}
return SUCCESS;
}
@@ -822,7 +834,17 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
return NULL;
case '$':
*buf_len = atoi(inbuf + 1);
{
char *endptr;
long long n;
errno = 0;
n = strtoll(inbuf + 1, &endptr, 10);
if (endptr == inbuf + 1 || errno == ERANGE || n < -1 || n > INT_MAX) {
return NULL;
}
*buf_len = (int)n;
}
return redis_sock_read_bulk_reply(redis_sock, *buf_len);
case '*':
@@ -4567,7 +4589,15 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type,
}
/* Set our size response */
*reply_info = atol(inbuf);
{
char *endptr;
errno = 0;
*reply_info = strtol(inbuf, &endptr, 10);
if (endptr == inbuf || errno == ERANGE) {
return -1;
}
}
} else {
/* Always initialize to prevent UB */
*reply_info = 0;