diff --git a/src/db.c b/src/db.c index e20ff5dcf..85ea43092 100644 --- a/src/db.c +++ b/src/db.c @@ -1223,23 +1223,44 @@ void scanGenericCommand(client *c, robj *o, unsigned long long cursor) { } } while (cursor && maxiterations-- && data.sampled < count); } else if (o->type == OBJ_SET) { + unsigned long array_reply_len = 0; + void *replylen = NULL; + listRelease(keys); char *str; char buf[LONG_STR_SIZE]; size_t len; int64_t llele; + /* Reply to the client. */ + addReplyArrayLen(c, 2); + /* Cursor is always 0 given we iterate over all set */ + addReplyBulkLongLong(c,0); + /* If there is no pattern the length is the entire set size, otherwise we defer the reply size */ + if (use_pattern) + replylen = addReplyDeferredLen(c); + else { + array_reply_len = setTypeSize(o); + addReplyArrayLen(c, array_reply_len); + } + setTypeIterator *si = setTypeInitIterator(o); + unsigned long cur_length = 0; while (setTypeNext(si, &str, &len, &llele) != -1) { if (str == NULL) { len = ll2string(buf, sizeof(buf), llele); } char *key = str ? str : buf; - if (use_pattern && !stringmatchlen(pat, sdslen(pat), key, len, 0)) { + if (use_pattern && !stringmatchlen(pat, patlen, key, len, 0)) { continue; } - listAddNodeTail(keys, sdsnewlen(key, len)); + addReplyBulkCBuffer(c, key, len); + cur_length++; } setTypeReleaseIterator(si); - cursor = 0; + if (use_pattern) + setDeferredArrayLen(c,replylen,cur_length); + else + serverAssert(cur_length == array_reply_len); /* fail on corrupt data */ + return; } else if ((o->type == OBJ_HASH || o->type == OBJ_ZSET) && o->encoding == OBJ_ENCODING_LISTPACK) { diff --git a/tests/integration/corrupt-dump.tcl b/tests/integration/corrupt-dump.tcl index 273ab4fb9..3225677fa 100644 --- a/tests/integration/corrupt-dump.tcl +++ b/tests/integration/corrupt-dump.tcl @@ -897,5 +897,17 @@ test {corrupt payload: fuzzer findings - set with invalid length causes smembers } } +test {corrupt payload: fuzzer findings - set with invalid length causes sscan to hang} { + start_server [list overrides [list loglevel verbose use-exit-on-panic yes crash-memcheck-enabled no] ] { + # In the past, it generated a broken protocol and left the client hung in smembers + r config set sanitize-dump-payload no + assert_equal {OK} [r restore _set 0 "\x14\x16\x16\x00\x00\x00\x0c\x00\x81\x61\x02\x81\x62\x02\x81\x63\x02\x01\x01\x02\x01\x03\x01\xff\x0c\x00\x91\x00\x56\x73\xc1\x82\xd5\xbd" replace] + assert_encoding listpack _set + catch { r SSCAN _set 0 } err + assert_equal [count_log_message 0 "crashed by signal"] 0 + assert_equal [count_log_message 0 "ASSERTION FAILED"] 1 + } +} + } ;# tags