From 2aea8fb68f5c58e71652e31a2fafc216fb3e4be8 Mon Sep 17 00:00:00 2001 From: Zz <2631992879@qq.com> Date: Tue, 26 Mar 2024 13:11:37 +0800 Subject: [PATCH] fix: variable length parameter judge error (#137) Co-authored-by: Lukas Maas --- libs/server/Resp/Objects/HashCommands.cs | 91 ++++---- libs/server/Resp/Objects/ListCommands.cs | 75 ++++--- libs/server/Resp/Objects/SetCommands.cs | 259 ++++++++++++----------- 3 files changed, 232 insertions(+), 193 deletions(-) diff --git a/libs/server/Resp/Objects/HashCommands.cs b/libs/server/Resp/Objects/HashCommands.cs index 8842575640..3f467380cf 100644 --- a/libs/server/Resp/Objects/HashCommands.cs +++ b/libs/server/Resp/Objects/HashCommands.cs @@ -38,61 +38,70 @@ private unsafe bool HashSet(int count, byte* ptr, HashOperation hop, { ptr += hop == HashOperation.HSET ? 10 : (hop == HashOperation.HSETNX ? 12 : 11); - // Get the key for Hash - if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) - return false; - - if (NetworkSingleKeySlotVerify(key, false)) + if (((hop == HashOperation.HSET || hop == HashOperation.HMSET) + && (count < 3 || count % 2 != 0)) || + (hop == HashOperation.HSETNX && count != 4)) { - var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); - if (!DrainCommands(bufSpan, count)) - return false; - return true; + return AbortWithWrongNumberOfArguments(hop.ToString(), count); } + else + { + // Get the key for Hash + if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) + return false; - // Prepare input - var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); + if (NetworkSingleKeySlotVerify(key, false)) + { + var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); + if (!DrainCommands(bufSpan, count)) + return false; + return true; + } - // Save old values on buffer for possible revert - var save = *inputPtr; + // Prepare input + var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); - // Prepare length of header in input buffer - var inputLength = (int)(recvBufferPtr + bytesRead - ptr) + sizeof(ObjectInputHeader); + // Save old values on buffer for possible revert + var save = *inputPtr; - var inputCount = (count - 2) / 2; + // Prepare length of header in input buffer + var inputLength = (int)(recvBufferPtr + bytesRead - ptr) + sizeof(ObjectInputHeader); - // Prepare header in input buffer - inputPtr->header.type = GarnetObjectType.Hash; - inputPtr->header.HashOp = hop; - inputPtr->count = inputCount; - inputPtr->done = hashOpsCount; + var inputCount = (count - 2) / 2; - storageApi.HashSet(key, new ArgSlice((byte*)inputPtr, inputLength), out ObjectOutputHeader output); + // Prepare header in input buffer + inputPtr->header.type = GarnetObjectType.Hash; + inputPtr->header.HashOp = hop; + inputPtr->count = inputCount; + inputPtr->done = hashOpsCount; - *inputPtr = save; // reset input buffer + storageApi.HashSet(key, new ArgSlice((byte*)inputPtr, inputLength), out ObjectOutputHeader output); - hashItemsDoneCount += output.countDone; - hashOpsCount += output.opsDone; + *inputPtr = save; // reset input buffer - // Reset buffer and return if HSET did not process the entire command tokens - if (hashItemsDoneCount < inputCount) - return false; + hashItemsDoneCount += output.countDone; + hashOpsCount += output.opsDone; - // Move head, write result to output, reset session counters - ptr += output.bytesDone; - readHead = (int)(ptr - recvBufferPtr); + // Reset buffer and return if HSET did not process the entire command tokens + if (hashItemsDoneCount < inputCount) + return false; - if (hop == HashOperation.HMSET) - { - while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); - } - else - { - while (!RespWriteUtils.WriteInteger(hashOpsCount, ref dcurr, dend)) - SendAndReset(); - } + // Move head, write result to output, reset session counters + ptr += output.bytesDone; + readHead = (int)(ptr - recvBufferPtr); + if (hop == HashOperation.HMSET) + { + while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_OK, ref dcurr, dend)) + SendAndReset(); + } + else + { + while (!RespWriteUtils.WriteInteger(hashOpsCount, ref dcurr, dend)) + SendAndReset(); + } + } + hashItemsDoneCount = hashOpsCount = 0; return true; } diff --git a/libs/server/Resp/Objects/ListCommands.cs b/libs/server/Resp/Objects/ListCommands.cs index 4de0ecf5bb..9b4b6bc82a 100644 --- a/libs/server/Resp/Objects/ListCommands.cs +++ b/libs/server/Resp/Objects/ListCommands.cs @@ -202,50 +202,57 @@ private unsafe bool ListLength(int count, byte* ptr, ref TGarnetApi { ptr += 10; - // Get the key for List - if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) - return false; - - if (NetworkSingleKeySlotVerify(key, true)) + if (count != 2) { - var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); - if (!DrainCommands(bufSpan, count)) - return false; - return true; + return AbortWithWrongNumberOfArguments("LLEN", count); } + else + { + // Get the key for List + if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) + return false; - // Prepare input - var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); + if (NetworkSingleKeySlotVerify(key, true)) + { + var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); + if (!DrainCommands(bufSpan, count)) + return false; + return true; + } - // save old values - var save = *inputPtr; + // Prepare input + var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); - // Prepare length of header in input buffer - var inputLength = (int)(recvBufferPtr + bytesRead - ptr) + sizeof(ObjectInputHeader); + // save old values + var save = *inputPtr; - // Prepare header in input buffer - inputPtr->header.type = GarnetObjectType.List; - inputPtr->header.ListOp = ListOperation.LLEN; - inputPtr->count = count; - inputPtr->done = 0; + // Prepare length of header in input buffer + var inputLength = (int)(recvBufferPtr + bytesRead - ptr) + sizeof(ObjectInputHeader); - var status = storageApi.ListLength(key, new ArgSlice((byte*)inputPtr, inputLength), out var output); + // Prepare header in input buffer + inputPtr->header.type = GarnetObjectType.List; + inputPtr->header.ListOp = ListOperation.LLEN; + inputPtr->count = count; + inputPtr->done = 0; - //restore input buffer - *inputPtr = save; + var status = storageApi.ListLength(key, new ArgSlice((byte*)inputPtr, inputLength), out var output); - if (status == GarnetStatus.NOTFOUND) - { - while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); - } - else - { - // Process output - while (!RespWriteUtils.WriteInteger(output.countDone, ref dcurr, dend)) - SendAndReset(); - } + //restore input buffer + *inputPtr = save; + if (status == GarnetStatus.NOTFOUND) + { + while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) + SendAndReset(); + } + else + { + // Process output + while (!RespWriteUtils.WriteInteger(output.countDone, ref dcurr, dend)) + SendAndReset(); + } + } + // Move input head, write result to output readHead = (int)(ptr - recvBufferPtr); diff --git a/libs/server/Resp/Objects/SetCommands.cs b/libs/server/Resp/Objects/SetCommands.cs index f476c43b7a..3af16ba671 100644 --- a/libs/server/Resp/Objects/SetCommands.cs +++ b/libs/server/Resp/Objects/SetCommands.cs @@ -38,51 +38,59 @@ private unsafe bool SetAdd(int count, byte* ptr, ref TGarnetApi stor { ptr += 10; - // Get the key for the Set - if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) - return false; - - if (NetworkSingleKeySlotVerify(key, false)) + if (count < 3) { - var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); - if (!DrainCommands(bufSpan, count)) return false; - return true; + setItemsDoneCount = setOpsCount = 0; + return AbortWithWrongNumberOfArguments("SADD", count); } + else + { + // Get the key for the Set + if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) + return false; - var inputCount = count - 2; - // Prepare input - var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); + if (NetworkSingleKeySlotVerify(key, false)) + { + var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); + if (!DrainCommands(bufSpan, count)) return false; + return true; + } - // Save old values on buffer for possible revert - var save = *inputPtr; + var inputCount = count - 2; + // Prepare input + var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); - // Prepare length of header in input buffer - var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); + // Save old values on buffer for possible revert + var save = *inputPtr; - // Prepare header in input buffer - inputPtr->header.type = GarnetObjectType.Set; - inputPtr->header.SetOp = SetOperation.SADD; - inputPtr->count = inputCount; - inputPtr->done = setOpsCount; + // Prepare length of header in input buffer + var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); - storageApi.SetAdd(key, new ArgSlice((byte*)inputPtr, inputLength), out ObjectOutputHeader output); + // Prepare header in input buffer + inputPtr->header.type = GarnetObjectType.Set; + inputPtr->header.SetOp = SetOperation.SADD; + inputPtr->count = inputCount; + inputPtr->done = setOpsCount; - // Restore input buffer - *inputPtr = save; + storageApi.SetAdd(key, new ArgSlice((byte*)inputPtr, inputLength), out ObjectOutputHeader output); - setItemsDoneCount += output.countDone; - setOpsCount += output.opsDone; + // Restore input buffer + *inputPtr = save; - // Reset buffer and return if SADD is only partially done - if (setOpsCount < inputCount) - return false; + setItemsDoneCount += output.countDone; + setOpsCount += output.opsDone; - // Move head, write result to output, reset session counters - ptr += output.bytesDone; - readHead = (int)(ptr - recvBufferPtr); + // Reset buffer and return if SADD is only partially done + if (setOpsCount < inputCount) + return false; - while (!RespWriteUtils.WriteInteger(setItemsDoneCount, ref dcurr, dend)) - SendAndReset(); + // Move head, write result to output, reset session counters + ptr += output.bytesDone; + readHead = (int)(ptr - recvBufferPtr); + + while (!RespWriteUtils.WriteInteger(setItemsDoneCount, ref dcurr, dend)) + SendAndReset(); + } setItemsDoneCount = setOpsCount = 0; return true; @@ -103,63 +111,71 @@ private unsafe bool SetRemove(int count, byte* ptr, ref TGarnetApi s { ptr += 10; - // Get the key - if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) - return false; - - if (NetworkSingleKeySlotVerify(key, false)) + if (count < 3) { - var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); - if (!DrainCommands(bufSpan, count)) - return false; - return true; + setItemsDoneCount = setOpsCount = 0; + return AbortWithWrongNumberOfArguments("SREM", count); } - var inputCount = count - 2; // only identifiers + else + { + // Get the key + if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) + return false; - // Prepare input - var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); + if (NetworkSingleKeySlotVerify(key, false)) + { + var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); + if (!DrainCommands(bufSpan, count)) + return false; + return true; + } + var inputCount = count - 2; // only identifiers - // Save old values on buffer for possible revert - var save = *inputPtr; + // Prepare input + var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); - // Prepare length of header in input buffer - var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); + // Save old values on buffer for possible revert + var save = *inputPtr; - // Prepare header in input buffer - inputPtr->header.type = GarnetObjectType.Set; - inputPtr->header.SetOp = SetOperation.SREM; - inputPtr->count = inputCount; - inputPtr->done = setItemsDoneCount; + // Prepare length of header in input buffer + var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); - var status = storageApi.SetRemove(key, new ArgSlice((byte*)inputPtr, inputLength), out var output); + // Prepare header in input buffer + inputPtr->header.type = GarnetObjectType.Set; + inputPtr->header.SetOp = SetOperation.SREM; + inputPtr->count = inputCount; + inputPtr->done = setItemsDoneCount; - // Restore input buffer - *inputPtr = save; + var status = storageApi.SetRemove(key, new ArgSlice((byte*)inputPtr, inputLength), out var output); - if (status == GarnetStatus.NOTFOUND) - { - while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); - ReadLeftToken(count - 2, ref ptr); - } - else - { - setItemsDoneCount += output.countDone; - setOpsCount += output.opsDone; + // Restore input buffer + *inputPtr = save; - // Reset buffer and return if command is only partially done - if (setOpsCount < inputCount) - return false; + if (status == GarnetStatus.NOTFOUND) + { + while (!RespWriteUtils.WriteResponse(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) + SendAndReset(); + ReadLeftToken(count - 2, ref ptr); + } + else + { + setItemsDoneCount += output.countDone; + setOpsCount += output.opsDone; - // Move head, write result to output, reset session counters - ptr += output.bytesDone; + // Reset buffer and return if command is only partially done + if (setOpsCount < inputCount) + return false; - while (!RespWriteUtils.WriteInteger(setItemsDoneCount, ref dcurr, dend)) - SendAndReset(); + // Move head, write result to output, reset session counters + ptr += output.bytesDone; - setOpsCount = setItemsDoneCount = 0; - } + while (!RespWriteUtils.WriteInteger(setItemsDoneCount, ref dcurr, dend)) + SendAndReset(); + setOpsCount = setItemsDoneCount = 0; + } + } + readHead = (int)(ptr - recvBufferPtr); return true; } @@ -181,7 +197,6 @@ private unsafe bool SetLength(int count, byte* ptr, ref TGarnetApi s { setItemsDoneCount = setOpsCount = 0; return AbortWithWrongNumberOfArguments("SCARD", count); - } else { @@ -248,58 +263,66 @@ private unsafe bool SetMembers(int count, byte* ptr, ref TGarnetApi { ptr += 14; - // Get the key - if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) - return false; - - if (NetworkSingleKeySlotVerify(key, true)) + if (count < 1 || count > 2) { - var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); - if (!DrainCommands(bufSpan, count)) - return false; - return true; + setItemsDoneCount = setOpsCount = 0; + return AbortWithWrongNumberOfArguments("SMEMBERS", count); } + else + { + // Get the key + if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var key, ref ptr, recvBufferPtr + bytesRead)) + return false; - // Prepare input - var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); + if (NetworkSingleKeySlotVerify(key, true)) + { + var bufSpan = new ReadOnlySpan(recvBufferPtr, bytesRead); + if (!DrainCommands(bufSpan, count)) + return false; + return true; + } - // Save old values - var save = *inputPtr; + // Prepare input + var inputPtr = (ObjectInputHeader*)(ptr - sizeof(ObjectInputHeader)); - // Prepare length of header in input buffer - var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); + // Save old values + var save = *inputPtr; - // Prepare header in input buffer - inputPtr->header.type = GarnetObjectType.Set; - inputPtr->header.SetOp = SetOperation.SMEMBERS; - inputPtr->count = count; - inputPtr->done = setItemsDoneCount; + // Prepare length of header in input buffer + var inputLength = (int)(recvBufferPtr + bytesRead - (byte*)inputPtr); - // Prepare GarnetObjectStore output - var outputFooter = new GarnetObjectStoreOutput { spanByteAndMemory = new SpanByteAndMemory(dcurr, (int)(dend - dcurr)) }; + // Prepare header in input buffer + inputPtr->header.type = GarnetObjectType.Set; + inputPtr->header.SetOp = SetOperation.SMEMBERS; + inputPtr->count = count; + inputPtr->done = setItemsDoneCount; - var status = storageApi.SetMembers(key, new ArgSlice((byte*)inputPtr, inputLength), ref outputFooter); + // Prepare GarnetObjectStore output + var outputFooter = new GarnetObjectStoreOutput { spanByteAndMemory = new SpanByteAndMemory(dcurr, (int)(dend - dcurr)) }; - // Restore input buffer - *inputPtr = save; + var status = storageApi.SetMembers(key, new ArgSlice((byte*)inputPtr, inputLength), ref outputFooter); - switch (status) - { - case GarnetStatus.OK: - // Process output - var objOutputHeader = ProcessOutputWithHeader(outputFooter.spanByteAndMemory); - ptr += objOutputHeader.bytesDone; - setItemsDoneCount += objOutputHeader.countDone; - if (setItemsDoneCount > objOutputHeader.opsDone) - return false; - break; - case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.WriteEmptyArray(ref dcurr, dend)) - SendAndReset(); - ReadLeftToken(count - 2, ref ptr); - break; - } + // Restore input buffer + *inputPtr = save; + switch (status) + { + case GarnetStatus.OK: + // Process output + var objOutputHeader = ProcessOutputWithHeader(outputFooter.spanByteAndMemory); + ptr += objOutputHeader.bytesDone; + setItemsDoneCount += objOutputHeader.countDone; + if (setItemsDoneCount > objOutputHeader.opsDone) + return false; + break; + case GarnetStatus.NOTFOUND: + while (!RespWriteUtils.WriteEmptyArray(ref dcurr, dend)) + SendAndReset(); + ReadLeftToken(count - 2, ref ptr); + break; + } + } + // Reset session counters setItemsDoneCount = setOpsCount = 0;