mirror of
https://github.com/valkey-io/valkey.git
synced 2026-05-06 05:26:42 -04:00
495650676b
Fixes #2938 - Root cause: On 32-bit builds, off_t depended on include order. lrulfu.c included <stdint.h>/<stdbool.h> via lrulfu.h before server.h, so _FILE_OFFSET_BITS=64 (from fmacros.h) was not in effect when glibc headers were first seen. This made off_t 32-bit in that TU, while 64-bit elsewhere, causing LTO linker “type mismatch” warnings and possible misoptimization. - Changes: 1. Include fmacros.h first in src/lrulfu.h to ensure feature macros apply before any system header. 2. Add a compile-time check in src/server.h: static_assert(sizeof(off_t) >= 8, "off_t must be 64-bit; ensure _FILE_OFFSET_BITS=64 is in effect before system headers"); so we fail fast if include order regresses. - Why this works: fmacros.h defines _FILE_OFFSET_BITS 64 and related feature macros. Ensuring it is seen first gives a consistent 64-bit off_t across all TUs. The static_assert turns future include-order mistakes into early compile-time failures instead of link-time notes/warnings. - Testing: - Built on 32-bit Debian: no LTO type-mismatch at link, binaries produced successfully. Only GCC 11 ABI notes about _Atomic alignment (“note: … changed in GCC 11.1”), which are informational (-Wpsabi) and do not affect correctness. - Risk: very low; only header include order + a defensive assert. No runtime changes. - Address CI feedback: add fmacros.h to unit sources that include headers before server.h; --------- Signed-off-by: Ada-Church-Closure <2574094394@qq.com> Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech> Co-authored-by: Ada-Church-Closure <2574094394@qq.com> Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
360 lines
9.9 KiB
C
360 lines
9.9 KiB
C
#include "../fmacros.h"
|
|
#include <sys/mman.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "../config.h"
|
|
#include "../util.h"
|
|
#include "test_help.h"
|
|
|
|
#if defined(__linux__)
|
|
#include <sys/statfs.h>
|
|
#include <linux/magic.h>
|
|
#endif
|
|
|
|
int test_string2ll(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
char buf[32];
|
|
long long v;
|
|
|
|
/* May not start with +. */
|
|
valkey_strlcpy(buf, "+1", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
/* Leading space. */
|
|
valkey_strlcpy(buf, " 1", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
/* Trailing space. */
|
|
valkey_strlcpy(buf, "1 ", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
/* May not start with 0. */
|
|
valkey_strlcpy(buf, "01", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
valkey_strlcpy(buf, "-1", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == -1);
|
|
|
|
valkey_strlcpy(buf, "0", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 0);
|
|
|
|
valkey_strlcpy(buf, "1", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 1);
|
|
|
|
valkey_strlcpy(buf, "99", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 99);
|
|
|
|
valkey_strlcpy(buf, "-99", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == -99);
|
|
|
|
valkey_strlcpy(buf, "-9223372036854775808", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == LLONG_MIN);
|
|
|
|
valkey_strlcpy(buf, "-9223372036854775809", sizeof(buf)); /* overflow */
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
valkey_strlcpy(buf, "9223372036854775807", sizeof(buf));
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == LLONG_MAX);
|
|
|
|
valkey_strlcpy(buf, "9223372036854775808", sizeof(buf)); /* overflow */
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
valkey_strlcpy(buf, "18446744073709551615", sizeof(buf)); /* overflow */
|
|
TEST_ASSERT(string2ll(buf, strlen(buf), &v) == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_string2l(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
char buf[32];
|
|
long v;
|
|
|
|
/* May not start with +. */
|
|
valkey_strlcpy(buf, "+1", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 0);
|
|
|
|
/* May not start with 0. */
|
|
valkey_strlcpy(buf, "01", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 0);
|
|
|
|
valkey_strlcpy(buf, "-1", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == -1);
|
|
|
|
valkey_strlcpy(buf, "0", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 0);
|
|
|
|
valkey_strlcpy(buf, "1", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 1);
|
|
|
|
valkey_strlcpy(buf, "99", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == 99);
|
|
|
|
valkey_strlcpy(buf, "-99", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == -99);
|
|
|
|
#if LONG_MAX != LLONG_MAX
|
|
valkey_strlcpy(buf, "-2147483648", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == LONG_MIN);
|
|
|
|
valkey_strlcpy(buf, "-2147483649", sizeof(buf)); /* overflow */
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 0);
|
|
|
|
valkey_strlcpy(buf, "2147483647", sizeof(buf));
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 1);
|
|
TEST_ASSERT(v == LONG_MAX);
|
|
|
|
valkey_strlcpy(buf, "2147483648", sizeof(buf)); /* overflow */
|
|
TEST_ASSERT(string2l(buf, strlen(buf), &v) == 0);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_ll2string(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
char buf[32];
|
|
long long v;
|
|
int sz;
|
|
|
|
v = 0;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 1);
|
|
TEST_ASSERT(!strcmp(buf, "0"));
|
|
|
|
v = -1;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 2);
|
|
TEST_ASSERT(!strcmp(buf, "-1"));
|
|
|
|
v = 99;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 2);
|
|
TEST_ASSERT(!strcmp(buf, "99"));
|
|
|
|
v = -99;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 3);
|
|
TEST_ASSERT(!strcmp(buf, "-99"));
|
|
|
|
v = -2147483648;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 11);
|
|
TEST_ASSERT(!strcmp(buf, "-2147483648"));
|
|
|
|
v = LLONG_MIN;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 20);
|
|
TEST_ASSERT(!strcmp(buf, "-9223372036854775808"));
|
|
|
|
v = LLONG_MAX;
|
|
sz = ll2string(buf, sizeof buf, v);
|
|
TEST_ASSERT(sz == 19);
|
|
TEST_ASSERT(!strcmp(buf, "9223372036854775807"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_ld2string(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
char buf[32];
|
|
long double v;
|
|
int sz;
|
|
|
|
v = 0.0 / 0.0;
|
|
sz = ld2string(buf, sizeof(buf), v, LD_STR_AUTO);
|
|
TEST_ASSERT(sz == 3);
|
|
TEST_ASSERT(!strcmp(buf, "nan"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_fixedpoint_d2string(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
char buf[32];
|
|
double v;
|
|
int sz;
|
|
v = 0.0;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 6);
|
|
TEST_ASSERT(!strcmp(buf, "0.0000"));
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 1);
|
|
TEST_ASSERT(sz == 3);
|
|
TEST_ASSERT(!strcmp(buf, "0.0"));
|
|
/* set junk in buffer */
|
|
memset(buf, 'A', 32);
|
|
v = 0.0001;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 6);
|
|
TEST_ASSERT(buf[sz] == '\0');
|
|
TEST_ASSERT(!strcmp(buf, "0.0001"));
|
|
/* set junk in buffer */
|
|
memset(buf, 'A', 32);
|
|
v = 6.0642951598391699e-05;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 6);
|
|
TEST_ASSERT(buf[sz] == '\0');
|
|
TEST_ASSERT(!strcmp(buf, "0.0001"));
|
|
v = 0.01;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 6);
|
|
TEST_ASSERT(!strcmp(buf, "0.0100"));
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 1);
|
|
TEST_ASSERT(sz == 3);
|
|
TEST_ASSERT(!strcmp(buf, "0.0"));
|
|
v = -0.01;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 7);
|
|
TEST_ASSERT(!strcmp(buf, "-0.0100"));
|
|
v = -0.1;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 1);
|
|
TEST_ASSERT(sz == 4);
|
|
TEST_ASSERT(!strcmp(buf, "-0.1"));
|
|
v = 0.1;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 1);
|
|
TEST_ASSERT(sz == 3);
|
|
TEST_ASSERT(!strcmp(buf, "0.1"));
|
|
v = 0.01;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 17);
|
|
TEST_ASSERT(sz == 19);
|
|
TEST_ASSERT(!strcmp(buf, "0.01000000000000000"));
|
|
v = 10.01;
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 4);
|
|
TEST_ASSERT(sz == 7);
|
|
TEST_ASSERT(!strcmp(buf, "10.0100"));
|
|
/* negative tests */
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 18);
|
|
TEST_ASSERT(sz == 0);
|
|
sz = fixedpoint_d2string(buf, sizeof buf, v, 0);
|
|
TEST_ASSERT(sz == 0);
|
|
sz = fixedpoint_d2string(buf, 1, v, 1);
|
|
TEST_ASSERT(sz == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_version2num(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
TEST_ASSERT(version2num("7.2.5") == 0x070205);
|
|
TEST_ASSERT(version2num("255.255.255") == 0xffffff);
|
|
TEST_ASSERT(version2num("7.2.256") == -1);
|
|
TEST_ASSERT(version2num("7.2") == -1);
|
|
TEST_ASSERT(version2num("7.2.1.0") == -1);
|
|
TEST_ASSERT(version2num("1.-2.-3") == -1);
|
|
TEST_ASSERT(version2num("1.2.3-rc4") == -1);
|
|
TEST_ASSERT(version2num("") == -1);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
/* Since fadvise and mincore is only supported in specific platforms like
|
|
* Linux, we only verify the fadvise mechanism works in Linux */
|
|
static int cache_exist(int fd) {
|
|
unsigned char flag;
|
|
void *m = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
|
|
TEST_ASSERT(m);
|
|
TEST_ASSERT(mincore(m, 4096, &flag) == 0);
|
|
munmap(m, 4096);
|
|
/* the least significant bit of the byte will be set if the corresponding
|
|
* page is currently resident in memory */
|
|
return flag & 1;
|
|
}
|
|
#endif
|
|
|
|
int test_reclaimFilePageCache(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
|
|
/* The test is incompatible with valgrind, skip it. */
|
|
if (flags & UNIT_TEST_VALGRIND) return 0;
|
|
|
|
#if defined(__linux__)
|
|
struct statfs stats;
|
|
|
|
/* Check if /tmp is memory-backed (e.g., tmpfs) */
|
|
if (statfs("/tmp", &stats) == 0) {
|
|
if (stats.f_type != TMPFS_MAGIC) { // Not tmpfs, use /tmp
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
char *tmpfile = "/tmp/redis-reclaim-cache-test";
|
|
int fd = open(tmpfile, O_RDWR | O_CREAT, 0644);
|
|
TEST_ASSERT(fd >= 0);
|
|
|
|
/* test write file */
|
|
char buf[4] = "foo";
|
|
TEST_ASSERT(write(fd, buf, sizeof(buf)) > 0);
|
|
TEST_ASSERT(cache_exist(fd));
|
|
TEST_ASSERT(valkey_fsync(fd) == 0);
|
|
TEST_ASSERT(reclaimFilePageCache(fd, 0, 0) == 0);
|
|
TEST_ASSERT(!cache_exist(fd));
|
|
|
|
/* test read file */
|
|
TEST_ASSERT(pread(fd, buf, sizeof(buf), 0) > 0);
|
|
TEST_ASSERT(cache_exist(fd));
|
|
TEST_ASSERT(reclaimFilePageCache(fd, 0, 0) == 0);
|
|
TEST_ASSERT(!cache_exist(fd));
|
|
|
|
unlink(tmpfile);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int test_writePointerWithPadding(int argc, char **argv, int flags) {
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(flags);
|
|
|
|
unsigned char buf[8];
|
|
static int dummy;
|
|
void *ptr = &dummy;
|
|
size_t ptr_size = sizeof(ptr);
|
|
|
|
/* Write the pointer and pad to 8 bytes */
|
|
writePointerWithPadding(buf, ptr);
|
|
|
|
/* The first ptr_size bytes must match the raw pointer bytes */
|
|
unsigned char expected[sizeof(ptr)];
|
|
memcpy(expected, &ptr, ptr_size);
|
|
TEST_ASSERT(memcmp(buf, expected, ptr_size) == 0);
|
|
|
|
|
|
/* The remaining bytes (if any) must be zero */
|
|
for (size_t i = ptr_size; i < sizeof(buf); i++) {
|
|
TEST_ASSERT(buf[i] == 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|