Skip to content

Fix GETRANGE DivideByZeroException on empty-string value with negative start#1969

Merged
vazois merged 1 commit into
microsoft:mainfrom
hexonal:fix-getrange-empty-key-negative-range
Jul 22, 2026
Merged

Fix GETRANGE DivideByZeroException on empty-string value with negative start#1969
vazois merged 1 commit into
microsoft:mainfrom
hexonal:fix-getrange-empty-key-negative-range

Conversation

@hexonal

@hexonal hexonal commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

GETRANGE on a key holding an empty string, with a negative start such that start <= end (e.g. GETRANGE key -1 -1), crashes the connection with a DivideByZeroException instead of returning an empty string.

Root cause

NormalizeRange(int start, int end, int len) in libs/server/Storage/Functions/MainStore/PrivateMethods.cs does start %= len unconditionally inside its start < 0 branch. When len == 0 (empty string value), this is a modulo by zero.

Fix

Add an early guard returning (0, 0) when len == 0 — the same sentinel NormalizeRange already returns from its other "no valid range" branches (e.g. if (start > end) return (0, 0);), so it's consistent with the function's existing convention rather than a new special case. NormalizeRange has exactly one caller (GETRANGE's handling in CopyRespToWithInput), and (0, 0) correctly resolves to an empty RESP reply there.

Tests

Added GetRangeEmptyValueTest to test/standalone/Garnet.test/RespTests.cs, covering the crashing case (GETRANGE key -1 -1, GETRANGE key -1 0) alongside non-crashing empty-value cases (GETRANGE key 0 0, GETRANGE key 0 -1) to confirm all correctly return empty string. Confirmed the test reproduces a connection-level failure against the pre-fix code and passes with the fix.

* Fix `GETRANGE` on an empty-string value throwing `DivideByZeroException` (and dropping the connection) for a negative start offset.

…e start

NormalizeRange's start < 0 branch does start %= len unconditionally. When
the key holds an empty string (len == 0) and start is negative with
start <= end (e.g. GETRANGE key -1 -1), this divides/mods by zero and
crashes the connection instead of returning an empty string, which is what
real Redis returns for any range query against an empty value.

Fixed with an early len == 0 guard returning (0, 0) - the same sentinel
NormalizeRange already returns from its other empty-result paths.
Copilot AI review requested due to automatic review settings July 22, 2026 05:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a crash in Garnet’s GETRANGE handling for keys holding an empty string when start is negative (e.g., GETRANGE key -1 -1). The root cause is a modulo-by-zero in NormalizeRange(start, end, len) when len == 0, which previously could drop the client connection with a DivideByZeroException.

Changes:

  • Add an early len == 0 guard in NormalizeRange to return the existing (0, 0) “empty range” sentinel and avoid modulo-by-zero.
  • Add a regression test that exercises the previously-crashing cases (negative start) as well as other empty-value GETRANGE calls.

PR Title & Description Alignment

  • Title accurately reflects the implemented fix and the failure mode.
  • Description matches the actual code change (early guard in NormalizeRange) and includes concrete repro + test coverage details.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
libs/server/Storage/Functions/MainStore/PrivateMethods.cs Adds a len == 0 early-return in NormalizeRange to prevent modulo-by-zero for empty string values in GETRANGE.
test/standalone/Garnet.test/RespTests.cs Adds GetRangeEmptyValueTest covering GETRANGE on an empty string with negative start indices and validating empty-string replies.

@vazois
vazois merged commit c7d6aec into microsoft:main Jul 22, 2026
161 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants