mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
config memory limits: handle values larger than (signed) LLONG_MAX (#9313)
This aims to solve the issue in CONFIG SET maxmemory can only set maxmemory to up to 9223372036854775807 (2^63) while the maxmemory should be ULLONG. Added a memtoull function to convert a string representing an amount of memory into the number of bytes (similar to memtoll but for ull). Also added ull2string to convert a ULLong to string (Similar to ll2string).
This commit is contained in:
parent
74590f8345
commit
641780a9c6
63
src/config.c
63
src/config.c
@ -559,8 +559,8 @@ void loadServerConfigFromString(char *config) {
|
||||
"an invalid one, or 'master' which has no buffer limits.";
|
||||
goto loaderr;
|
||||
}
|
||||
hard = memtoll(argv[2],NULL);
|
||||
soft = memtoll(argv[3],NULL);
|
||||
hard = memtoull(argv[2],NULL);
|
||||
soft = memtoull(argv[3],NULL);
|
||||
soft_seconds = atoi(argv[4]);
|
||||
if (soft_seconds < 0) {
|
||||
err = "Negative number of seconds in soft limit is invalid";
|
||||
@ -744,8 +744,8 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) {
|
||||
|
||||
#define config_set_memory_field(_name,_var) \
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
|
||||
ll = memtoll(o->ptr,&err); \
|
||||
if (err || ll < 0) goto badfmt; \
|
||||
ll = memtoull(o->ptr,&err); \
|
||||
if (err) goto badfmt; \
|
||||
_var = ll;
|
||||
|
||||
#define config_set_special_field(_name) \
|
||||
@ -855,22 +855,26 @@ void configSetCommand(client *c) {
|
||||
* whole configuration string or accept it all, even if a single
|
||||
* error in a single client class is present. */
|
||||
for (j = 0; j < vlen; j++) {
|
||||
long val;
|
||||
|
||||
if ((j % 4) == 0) {
|
||||
int class = getClientTypeByName(v[j]);
|
||||
if (class == -1 || class == CLIENT_TYPE_MASTER) {
|
||||
sdsfreesplitres(v,vlen);
|
||||
goto badfmt;
|
||||
}
|
||||
if (class == -1 || class == CLIENT_TYPE_MASTER)
|
||||
break;
|
||||
} else if ((j % 4) == 3) {
|
||||
char *endptr;
|
||||
long l = strtoll(v[j],&endptr,10);
|
||||
if (l < 0 || *endptr != '\0')
|
||||
break;
|
||||
} else {
|
||||
val = memtoll(v[j], &err);
|
||||
if (err || val < 0) {
|
||||
sdsfreesplitres(v,vlen);
|
||||
goto badfmt;
|
||||
}
|
||||
memtoull(v[j], &err);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < vlen) {
|
||||
sdsfreesplitres(v,vlen);
|
||||
goto badfmt;
|
||||
}
|
||||
|
||||
/* Finally set the new config */
|
||||
for (j = 0; j < vlen; j += 4) {
|
||||
int class;
|
||||
@ -878,8 +882,8 @@ void configSetCommand(client *c) {
|
||||
int soft_seconds;
|
||||
|
||||
class = getClientTypeByName(v[j]);
|
||||
hard = memtoll(v[j+1],NULL);
|
||||
soft = memtoll(v[j+2],NULL);
|
||||
hard = memtoull(v[j+1],NULL);
|
||||
soft = memtoull(v[j+2],NULL);
|
||||
soft_seconds = strtoll(v[j+3],NULL,10);
|
||||
|
||||
server.client_obuf_limits[class].hard_limit_bytes = hard;
|
||||
@ -2123,22 +2127,22 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int numericConfigSet(typeData data, sds value, int update, const char **err) {
|
||||
long long ll, prev = 0;
|
||||
if (data.numeric.is_memory) {
|
||||
int memerr;
|
||||
ll = memtoll(value, &memerr);
|
||||
if (memerr || ll < 0) {
|
||||
ll = memtoull(value, &memerr);
|
||||
if (memerr) {
|
||||
*err = "argument must be a memory value";
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!string2ll(value, sdslen(value),&ll)) {
|
||||
if (!string2ll(value, sdslen(value), &ll)) {
|
||||
*err = "argument couldn't be parsed into an integer" ;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!numericBoundaryCheck(data, ll, err))
|
||||
return 0;
|
||||
|
||||
@ -2157,12 +2161,21 @@ static int numericConfigSet(typeData data, sds value, int update, const char **e
|
||||
|
||||
static void numericConfigGet(client *c, typeData data) {
|
||||
char buf[128];
|
||||
long long value = 0;
|
||||
if (data.numeric.is_memory) {
|
||||
unsigned long long value = 0;
|
||||
|
||||
GET_NUMERIC_TYPE(value)
|
||||
|
||||
GET_NUMERIC_TYPE(value)
|
||||
ull2string(buf, sizeof(buf), value);
|
||||
addReplyBulkCString(c, buf);
|
||||
} else{
|
||||
long long value = 0;
|
||||
|
||||
GET_NUMERIC_TYPE(value)
|
||||
|
||||
ll2string(buf, sizeof(buf), value);
|
||||
addReplyBulkCString(c, buf);
|
||||
ll2string(buf, sizeof(buf), value);
|
||||
addReplyBulkCString(c, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
|
||||
|
66
src/util.c
66
src/util.c
@ -185,18 +185,19 @@ int stringmatchlen_fuzz_test(void) {
|
||||
return total_matches;
|
||||
}
|
||||
|
||||
|
||||
/* Convert a string representing an amount of memory into the number of
|
||||
* bytes, so for instance memtoll("1Gb") will return 1073741824 that is
|
||||
* bytes, so for instance memtoull("1Gb") will return 1073741824 that is
|
||||
* (1024*1024*1024).
|
||||
*
|
||||
* On parsing error, if *err is not NULL, it's set to 1, otherwise it's
|
||||
* set to 0. On error the function return value is 0, regardless of the
|
||||
* fact 'err' is NULL or not. */
|
||||
long long memtoll(const char *p, int *err) {
|
||||
unsigned long long memtoull(const char *p, int *err) {
|
||||
const char *u;
|
||||
char buf[128];
|
||||
long mul; /* unit multiplier */
|
||||
long long val;
|
||||
unsigned long long val;
|
||||
unsigned int digits;
|
||||
|
||||
if (err) *err = 0;
|
||||
@ -236,7 +237,7 @@ long long memtoll(const char *p, int *err) {
|
||||
|
||||
char *endptr;
|
||||
errno = 0;
|
||||
val = strtoll(buf,&endptr,10);
|
||||
val = strtoull(buf,&endptr,10);
|
||||
if ((val == 0 && errno == EINVAL) || *endptr != '\0') {
|
||||
if (err) *err = 1;
|
||||
return 0;
|
||||
@ -307,26 +308,12 @@ uint32_t sdigits10(int64_t v) {
|
||||
|
||||
/* Convert a long long into a string. Returns the number of
|
||||
* characters needed to represent the number.
|
||||
* If the buffer is not big enough to store the string, 0 is returned.
|
||||
*
|
||||
* Based on the following article (that apparently does not provide a
|
||||
* novel approach but only publicizes an already used technique):
|
||||
*
|
||||
* https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
|
||||
*
|
||||
* Modified in order to handle signed integers since the original code was
|
||||
* designed for unsigned integers. */
|
||||
* If the buffer is not big enough to store the string, 0 is returned. */
|
||||
int ll2string(char *dst, size_t dstlen, long long svalue) {
|
||||
static const char digits[201] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
int negative;
|
||||
unsigned long long value;
|
||||
int negative = 0;
|
||||
|
||||
/* The main loop works with 64bit unsigned integers for simplicity, so
|
||||
/* The ull2string function with 64bit unsigned integers for simplicity, so
|
||||
* we convert the number here and remember if it is negative. */
|
||||
if (svalue < 0) {
|
||||
if (svalue != LLONG_MIN) {
|
||||
@ -334,20 +321,45 @@ int ll2string(char *dst, size_t dstlen, long long svalue) {
|
||||
} else {
|
||||
value = ((unsigned long long) LLONG_MAX)+1;
|
||||
}
|
||||
if (dstlen < 2)
|
||||
return 0;
|
||||
negative = 1;
|
||||
dst[0] = '-';
|
||||
dst++;
|
||||
dstlen--;
|
||||
} else {
|
||||
value = svalue;
|
||||
negative = 0;
|
||||
}
|
||||
|
||||
/* Converts the unsigned long long value to string*/
|
||||
int length = ull2string(dst, dstlen, value);
|
||||
if (length == 0) return 0;
|
||||
return length + negative;
|
||||
}
|
||||
|
||||
/* Convert a unsigned long long into a string. Returns the number of
|
||||
* characters needed to represent the number.
|
||||
* If the buffer is not big enough to store the string, 0 is returned.
|
||||
*
|
||||
* Based on the following article (that apparently does not provide a
|
||||
* novel approach but only publicizes an already used technique):
|
||||
*
|
||||
* https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920 */
|
||||
int ull2string(char *dst, size_t dstlen, unsigned long long value) {
|
||||
static const char digits[201] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
|
||||
/* Check length. */
|
||||
uint32_t const length = digits10(value)+negative;
|
||||
uint32_t length = digits10(value);
|
||||
if (length >= dstlen) return 0;
|
||||
|
||||
/* Null term. */
|
||||
uint32_t next = length;
|
||||
dst[next] = '\0';
|
||||
next--;
|
||||
uint32_t next = length - 1;
|
||||
dst[next + 1] = '\0';
|
||||
while (value >= 100) {
|
||||
int const i = (value % 100) * 2;
|
||||
value /= 100;
|
||||
@ -365,8 +377,6 @@ int ll2string(char *dst, size_t dstlen, long long svalue) {
|
||||
dst[next - 1] = digits[i];
|
||||
}
|
||||
|
||||
/* Add sign. */
|
||||
if (negative) dst[0] = '-';
|
||||
return length;
|
||||
}
|
||||
|
||||
|
@ -48,12 +48,13 @@ typedef enum {
|
||||
int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);
|
||||
int stringmatch(const char *p, const char *s, int nocase);
|
||||
int stringmatchlen_fuzz_test(void);
|
||||
long long memtoll(const char *p, int *err);
|
||||
unsigned long long memtoull(const char *p, int *err);
|
||||
const char *mempbrk(const char *s, size_t len, const char *chars, size_t charslen);
|
||||
char *memmapchars(char *s, size_t len, const char *from, const char *to, size_t setlen);
|
||||
uint32_t digits10(uint64_t v);
|
||||
uint32_t sdigits10(int64_t v);
|
||||
int ll2string(char *s, size_t len, long long value);
|
||||
int ull2string(char *s, size_t len, unsigned long long value);
|
||||
int string2ll(const char *s, size_t slen, long long *value);
|
||||
int string2ull(const char *s, unsigned long long *value);
|
||||
int string2l(const char *s, size_t slen, long *value);
|
||||
|
Loading…
Reference in New Issue
Block a user