Skip to content

fix: validate key count before allocating result in keyspec#3598

Merged
murphyjacob4 merged 1 commit into
valkey-io:unstablefrom
murphyjacob4:keyspec-num
Apr 30, 2026
Merged

fix: validate key count before allocating result in keyspec#3598
murphyjacob4 merged 1 commit into
valkey-io:unstablefrom
murphyjacob4:keyspec-num

Conversation

@murphyjacob4

Copy link
Copy Markdown
Contributor

In getKeysUsingKeySpecs, when extracting keys based on the KSPEC_FK_KEYNUM spec (like in the EVAL command), the server read the number of keys from the arguments and calculated the expected end index.

However, it called getKeysPrepareResult to allocate memory for the result array before validating whether last was within the bounds of the actual arguments provided.

If a client sent a command with a huge declared number of keys (e.g., COMMAND GETKEYS EVAL "return 1" 2147483647 key1), the server would allocate a massive amount of memory. Since vm.overcommit_memory is recommended, this allocation would NOT normally have triggered OOM (we never wrote to it so there is no physical memory allocated), but if you disable overcommit, this could trigger an OOM.

You can reproduce it with:

$ prlimit --as=1073741824 src/valkey-server --save ""
...
384270:M 30 Apr 2026 04:27:24.456 * Ready to accept connections tcp

...
<in valkey-cli>
127.0.0.1:6379> command getkeys eval "return 1" 2147483647 key1

...
<in server log>
384270:M 30 Apr 2026 04:29:26.950 # Out Of Memory allocating 17179869176 bytes!

Solution

  • Moved the bounds check if (last >= argc || last < first || first >= argc) to execute before the call to getKeysPrepareResult, preventing the large allocation on invalid input.
  • To further catch issues like this, protected against integer overflow during the calculation of last by using a long long temporary variable. If it exceeds INT_MAX or falls below INT_MIN, the spec is marked invalid immediately.

Signed-off-by: Jacob Murphy <jkmurphy@google.com>
@murphyjacob4 murphyjacob4 changed the title fix: validate key count before attempting to get keys from keyspec fix: validate key count before allocating result in keyspec Apr 30, 2026
@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.52%. Comparing base (98724dd) to head (1f1f56b).
⚠️ Report is 3 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #3598      +/-   ##
============================================
- Coverage     76.61%   76.52%   -0.09%     
============================================
  Files           160      160              
  Lines         80472    80471       -1     
============================================
- Hits          61652    61580      -72     
- Misses        18820    18891      +71     
Files with missing lines Coverage Δ
src/db.c 94.51% <100.00%> (+<0.01%) ⬆️

... and 22 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@murphyjacob4 murphyjacob4 merged commit 81639e3 into valkey-io:unstable Apr 30, 2026
60 checks passed
lucasyonge pushed a commit that referenced this pull request May 11, 2026
In `getKeysUsingKeySpecs`, when extracting keys based on the
`KSPEC_FK_KEYNUM `spec (like in the `EVAL` command), the server read the
number of keys from the arguments and calculated the expected end index.

However, it called `getKeysPrepareResult` to allocate memory for the
result array before validating whether last was within the bounds of the
actual arguments provided.

If a client sent a command with a huge declared number of keys (e.g.,
`COMMAND GETKEYS EVAL "return 1" 2147483647 key1`), the server would
allocate a massive amount of memory. Since `vm.overcommit_memory` is
recommended, this allocation would NOT normally have triggered OOM (we
never wrote to it so there is no physical memory allocated), but if you
disable overcommit, this could trigger an OOM.

You can reproduce it with:

```
$ prlimit --as=1073741824 src/valkey-server --save ""
...
384270:M 30 Apr 2026 04:27:24.456 * Ready to accept connections tcp

...
<in valkey-cli>
127.0.0.1:6379> command getkeys eval "return 1" 2147483647 key1

...
<in server log>
384270:M 30 Apr 2026 04:29:26.950 # Out Of Memory allocating 17179869176 bytes!
```

## Solution

* Moved the bounds check `if (last >= argc || last < first || first >=
argc)` to execute before the call to `getKeysPrepareResult`, preventing
the large allocation on invalid input.
* To further catch issues like this, protected against integer overflow
during the calculation of last by using a long long temporary variable.
If it exceeds INT_MAX or falls below INT_MIN, the spec is marked invalid
immediately.

Signed-off-by: Jacob Murphy <jkmurphy@google.com>
lucasyonge pushed a commit that referenced this pull request May 12, 2026
In `getKeysUsingKeySpecs`, when extracting keys based on the
`KSPEC_FK_KEYNUM `spec (like in the `EVAL` command), the server read the
number of keys from the arguments and calculated the expected end index.

However, it called `getKeysPrepareResult` to allocate memory for the
result array before validating whether last was within the bounds of the
actual arguments provided.

If a client sent a command with a huge declared number of keys (e.g.,
`COMMAND GETKEYS EVAL "return 1" 2147483647 key1`), the server would
allocate a massive amount of memory. Since `vm.overcommit_memory` is
recommended, this allocation would NOT normally have triggered OOM (we
never wrote to it so there is no physical memory allocated), but if you
disable overcommit, this could trigger an OOM.

You can reproduce it with:

```
$ prlimit --as=1073741824 src/valkey-server --save ""
...
384270:M 30 Apr 2026 04:27:24.456 * Ready to accept connections tcp

...
<in valkey-cli>
127.0.0.1:6379> command getkeys eval "return 1" 2147483647 key1

...
<in server log>
384270:M 30 Apr 2026 04:29:26.950 # Out Of Memory allocating 17179869176 bytes!
```

## Solution

* Moved the bounds check `if (last >= argc || last < first || first >=
argc)` to execute before the call to `getKeysPrepareResult`, preventing
the large allocation on invalid input.
* To further catch issues like this, protected against integer overflow
during the calculation of last by using a long long temporary variable.
If it exceeds INT_MAX or falls below INT_MIN, the spec is marked invalid
immediately.

Signed-off-by: Jacob Murphy <jkmurphy@google.com>
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.

2 participants