mirror of https://mirror.osredm.com/root/redis.git
Fix use-after-free when diskless load config is not swapdb (#13887)
CI / build-macos-latest (push) Waiting to run
Details
CI / test-sanitizer-address (push) Failing after 32s
Details
CI / build-debian-old (push) Failing after 31s
Details
CI / build-centos-jemalloc (push) Failing after 32s
Details
CI / build-libc-malloc (push) Failing after 32s
Details
CI / build-32bit (push) Failing after 32s
Details
CI / build-old-chain-jemalloc (push) Failing after 32s
Details
Codecov / code-coverage (push) Failing after 31s
Details
External Server Tests / test-external-standalone (push) Failing after 32s
Details
External Server Tests / test-external-cluster (push) Failing after 32s
Details
External Server Tests / test-external-nodebug (push) Failing after 32s
Details
CI / test-ubuntu-latest (push) Failing after 1m37s
Details
Spellcheck / Spellcheck (push) Failing after 32s
Details
CI / build-macos-latest (push) Waiting to run
Details
CI / test-sanitizer-address (push) Failing after 32s
Details
CI / build-debian-old (push) Failing after 31s
Details
CI / build-centos-jemalloc (push) Failing after 32s
Details
CI / build-libc-malloc (push) Failing after 32s
Details
CI / build-32bit (push) Failing after 32s
Details
CI / build-old-chain-jemalloc (push) Failing after 32s
Details
Codecov / code-coverage (push) Failing after 31s
Details
External Server Tests / test-external-standalone (push) Failing after 32s
Details
External Server Tests / test-external-cluster (push) Failing after 32s
Details
External Server Tests / test-external-nodebug (push) Failing after 32s
Details
CI / test-ubuntu-latest (push) Failing after 1m37s
Details
Spellcheck / Spellcheck (push) Failing after 32s
Details
When the diskless load configuration is set to on-empty-db, we retain a pointer to the function library context. When emptyData() is called, it frees this function library context pointer, leading to a use-after-free situation. I refactored code to ensure that emptyData() is called first, followed by retrieving the valid pointer to the function library context. Refactored code should not introduce any runtime implications. Bug introduced by https://github.com/redis/redis/pull/13495 (Redis 8.0) Co-authored-by: Oran Agra <oran@redislabs.com>
This commit is contained in:
parent
981aa5c12f
commit
a0da8390a2
|
@ -2014,8 +2014,6 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
char buf[PROTO_IOBUF_LEN];
|
char buf[PROTO_IOBUF_LEN];
|
||||||
ssize_t nread, readlen, nwritten;
|
ssize_t nread, readlen, nwritten;
|
||||||
int use_diskless_load = useDisklessLoad();
|
int use_diskless_load = useDisklessLoad();
|
||||||
redisDb *diskless_load_tempDb = NULL;
|
|
||||||
functionsLibCtx* temp_functions_lib_ctx = NULL;
|
|
||||||
int rdbchannel = (conn == server.repl_rdb_transfer_s);
|
int rdbchannel = (conn == server.repl_rdb_transfer_s);
|
||||||
int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :
|
int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :
|
||||||
EMPTYDB_NO_FLAGS;
|
EMPTYDB_NO_FLAGS;
|
||||||
|
@ -2208,17 +2206,9 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
killRDBChild();
|
killRDBChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_diskless_load && server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
|
/* Attach to the new master immediately if we are not using swapdb. */
|
||||||
/* Initialize empty tempDb dictionaries. */
|
if (!use_diskless_load || server.repl_diskless_load != REPL_DISKLESS_LOAD_SWAPDB)
|
||||||
diskless_load_tempDb = disklessLoadInitTempDb();
|
|
||||||
temp_functions_lib_ctx = functionsLibCtxCreate();
|
|
||||||
|
|
||||||
moduleFireServerEvent(REDISMODULE_EVENT_REPL_ASYNC_LOAD,
|
|
||||||
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED,
|
|
||||||
NULL);
|
|
||||||
} else {
|
|
||||||
replicationAttachToNewMaster();
|
replicationAttachToNewMaster();
|
||||||
}
|
|
||||||
|
|
||||||
/* Before loading the DB into memory we need to delete the readable
|
/* Before loading the DB into memory we need to delete the readable
|
||||||
* handler, otherwise it will get called recursively since
|
* handler, otherwise it will get called recursively since
|
||||||
|
@ -2235,6 +2225,9 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
int asyncLoading = 0;
|
int asyncLoading = 0;
|
||||||
|
|
||||||
if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
|
if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
|
||||||
|
moduleFireServerEvent(REDISMODULE_EVENT_REPL_ASYNC_LOAD,
|
||||||
|
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED,
|
||||||
|
NULL);
|
||||||
/* Async loading means we continue serving read commands during full resync, and
|
/* Async loading means we continue serving read commands during full resync, and
|
||||||
* "swap" the new db with the old db only when loading is done.
|
* "swap" the new db with the old db only when loading is done.
|
||||||
* It is enabled only on SWAPDB diskless replication when master replication ID hasn't changed,
|
* It is enabled only on SWAPDB diskless replication when master replication ID hasn't changed,
|
||||||
|
@ -2243,15 +2236,9 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
if (memcmp(server.replid, server.master_replid, CONFIG_RUN_ID_SIZE) == 0) {
|
if (memcmp(server.replid, server.master_replid, CONFIG_RUN_ID_SIZE) == 0) {
|
||||||
asyncLoading = 1;
|
asyncLoading = 1;
|
||||||
}
|
}
|
||||||
dbarray = diskless_load_tempDb;
|
|
||||||
functions_lib_ctx = temp_functions_lib_ctx;
|
|
||||||
} else {
|
|
||||||
dbarray = server.db;
|
|
||||||
functions_lib_ctx = functionsLibCtxGetCurrent();
|
|
||||||
functionsLibCtxClear(functions_lib_ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disklessLoadingRio = &rdb;
|
/* Empty db */
|
||||||
loadingSetFlags(NULL, server.repl_transfer_size, asyncLoading);
|
loadingSetFlags(NULL, server.repl_transfer_size, asyncLoading);
|
||||||
if (server.repl_diskless_load != REPL_DISKLESS_LOAD_SWAPDB) {
|
if (server.repl_diskless_load != REPL_DISKLESS_LOAD_SWAPDB) {
|
||||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Flushing old data");
|
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Flushing old data");
|
||||||
|
@ -2262,7 +2249,17 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
}
|
}
|
||||||
loadingFireEvent(RDBFLAGS_REPLICATION);
|
loadingFireEvent(RDBFLAGS_REPLICATION);
|
||||||
|
|
||||||
|
if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
|
||||||
|
dbarray = disklessLoadInitTempDb();
|
||||||
|
functions_lib_ctx = functionsLibCtxCreate();
|
||||||
|
} else {
|
||||||
|
dbarray = server.db;
|
||||||
|
functions_lib_ctx = functionsLibCtxGetCurrent();
|
||||||
|
functionsLibCtxClear(functions_lib_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
rioInitWithConn(&rdb,conn,server.repl_transfer_size);
|
rioInitWithConn(&rdb,conn,server.repl_transfer_size);
|
||||||
|
disklessLoadingRio = &rdb;
|
||||||
|
|
||||||
/* Put the socket in blocking mode to simplify RDB transfer.
|
/* Put the socket in blocking mode to simplify RDB transfer.
|
||||||
* We'll restore it when the RDB is received. */
|
* We'll restore it when the RDB is received. */
|
||||||
|
@ -2297,8 +2294,8 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED,
|
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
disklessLoadDiscardTempDb(diskless_load_tempDb);
|
disklessLoadDiscardTempDb(dbarray);
|
||||||
functionsLibCtxFree(temp_functions_lib_ctx);
|
functionsLibCtxFree(functions_lib_ctx);
|
||||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Discarding temporary DB in background");
|
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Discarding temporary DB in background");
|
||||||
} else {
|
} else {
|
||||||
/* Remove the half-loaded data in case we started with an empty replica. */
|
/* Remove the half-loaded data in case we started with an empty replica. */
|
||||||
|
@ -2328,17 +2325,17 @@ void readSyncBulkPayload(connection *conn) {
|
||||||
replicationAttachToNewMaster();
|
replicationAttachToNewMaster();
|
||||||
|
|
||||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Swapping active DB with loaded DB");
|
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Swapping active DB with loaded DB");
|
||||||
swapMainDbWithTempDb(diskless_load_tempDb);
|
swapMainDbWithTempDb(dbarray);
|
||||||
|
|
||||||
/* swap existing functions ctx with the temporary one */
|
/* swap existing functions ctx with the temporary one */
|
||||||
functionsLibCtxSwapWithCurrent(temp_functions_lib_ctx);
|
functionsLibCtxSwapWithCurrent(functions_lib_ctx);
|
||||||
|
|
||||||
moduleFireServerEvent(REDISMODULE_EVENT_REPL_ASYNC_LOAD,
|
moduleFireServerEvent(REDISMODULE_EVENT_REPL_ASYNC_LOAD,
|
||||||
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED,
|
REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
/* Delete the old db as it's useless now. */
|
/* Delete the old db as it's useless now. */
|
||||||
disklessLoadDiscardTempDb(diskless_load_tempDb);
|
disklessLoadDiscardTempDb(dbarray);
|
||||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Discarding old DB in background");
|
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Discarding old DB in background");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1619,3 +1619,32 @@ start_server {tags {"repl external:skip"}} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_server {tags {"repl external:skip"}} {
|
||||||
|
set replica [srv 0 client]
|
||||||
|
start_server {} {
|
||||||
|
set master [srv 0 client]
|
||||||
|
set master_host [srv 0 host]
|
||||||
|
set master_port [srv 0 port]
|
||||||
|
|
||||||
|
test "Test replication with functions when repl-diskless-load is set to on-empty-db" {
|
||||||
|
$replica config set repl-diskless-load on-empty-db
|
||||||
|
|
||||||
|
populate 10 master 10
|
||||||
|
$master function load {#!lua name=test
|
||||||
|
redis.register_function{function_name='func1', callback=function() return 'hello' end, flags={'no-writes'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
$replica replicaof $master_host $master_port
|
||||||
|
|
||||||
|
# Wait until replication is completed
|
||||||
|
wait_replica_online $master 0 1000 100
|
||||||
|
wait_for_ofs_sync $master $replica
|
||||||
|
|
||||||
|
# Sanity check
|
||||||
|
assert_equal [$replica fcall func1 0] "hello"
|
||||||
|
assert_morethan [$replica dbsize] 0
|
||||||
|
assert_equal [$master debug digest] [$replica debug digest]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue