diff --git a/libs/server/Objects/List/ListObjectImpl.cs b/libs/server/Objects/List/ListObjectImpl.cs index 561c1a0ece1..c26ab4df38d 100644 --- a/libs/server/Objects/List/ListObjectImpl.cs +++ b/libs/server/Objects/List/ListObjectImpl.cs @@ -49,7 +49,10 @@ private void ListRemove(ref ObjectInput input, ref ObjectOutput output) var fromHeadToTail = count > 0; var currentNode = fromHeadToTail ? list.First : list.Last; - count = Math.Abs(count); + // |int.MinValue| does not fit in an int, so Math.Abs would throw. + // The loop below is bounded by the list length, which cannot exceed + // int.MaxValue, so clamping removes exactly the same elements. + count = count == int.MinValue ? int.MaxValue : Math.Abs(count); while (removedCount < count && currentNode != null) { var nextNode = fromHeadToTail ? currentNode.Next : currentNode.Previous; diff --git a/test/standalone/Garnet.test.collections/RespListTests.cs b/test/standalone/Garnet.test.collections/RespListTests.cs index 1e11eef1b5a..86349f549b1 100644 --- a/test/standalone/Garnet.test.collections/RespListTests.cs +++ b/test/standalone/Garnet.test.collections/RespListTests.cs @@ -371,6 +371,28 @@ public void BasicRPUSHAndLREM() ClassicAssert.IsFalse(exists); } + [Test] + public void LREMWithIntMinValueCountRemovesAllMatches() + { + using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); + var db = redis.GetDatabase(0); + + var key = "List_Test_LREM_MinValue"; + db.KeyDelete(key); + db.ListRightPush(key, ["a", "b", "a", "c", "a"]); + + // |int.MinValue| does not fit in an int; before the fix Math.Abs threw + // OverflowException out of ProcessMessages and the session was dropped. + var removed = db.ListRemove(key, "a", int.MinValue); + ClassicAssert.AreEqual(3, removed); + + var remaining = db.ListRange(key, 0, -1); + ClassicAssert.AreEqual(new RedisValue[] { "b", "c" }, remaining); + + // The connection must still be usable. + ClassicAssert.AreEqual("PONG", db.Execute("PING").ToString()); + } + [Test] public void MultiLPUSHAndLPOPV1() {