Skip to content

Fix ACL OOB for wrong-arity KEYNUM commands#14847

Merged
sundb merged 1 commit intoredis:unstablefrom
zzjas:fix-acl-arity-check
Mar 19, 2026
Merged

Fix ACL OOB for wrong-arity KEYNUM commands#14847
sundb merged 1 commit intoredis:unstablefrom
zzjas:fix-acl-arity-check

Conversation

@zzjas
Copy link
Copy Markdown
Contributor

@zzjas zzjas commented Mar 4, 2026

luaRedisAclCheckCmdPermissionsCommand and RM_ACLCheckCommandPermissions now call commandCheckArity() to check command arity before calling ACLCheckAllUserCommandPerm, matching the behavior of processCommand, scriptCall, and RM_Call. Without this, KEYNUM keyspec commands like EVAL with wrong arity cause out-of-bounds argv access during key extraction.

Also fix KEYNUM index calculation (first + keynumidx) and add a bounds check in genericGetKeys().

Add scripting and module ACL tests for wrong-arity EVAL to lock in the non-crashing behavior.

Fixes #14843


Note

Medium Risk
Touches command key-extraction and ACL-check paths used by modules and Lua scripting; mistakes could change authorization behavior or error handling for affected commands.

Overview
Prevents crashes during ACL checks on wrong-arity commands by validating command arity before ACL key/channel extraction in both the module API (RM_ACLCheckCommandPermissions) and Lua (redis.acl_check_cmd).

Hardens key discovery for KEYNUM key-specs by computing the key-count index relative to the resolved first argument and guarding against negative/out-of-range argv access, plus an early bounds check in genericGetKeys when the key-count argument is missing.

Adds regression tests ensuring wrong-arity EVAL fails safely (returns an error/denial) in module ACL checks and in redis.acl_check_cmd, rather than crashing.

Written by Cursor Bugbot for commit d3d1f31. This will update automatically on new commits. Configure here.

@jit-ci
Copy link
Copy Markdown

jit-ci Bot commented Mar 4, 2026

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 4, 2026

CLA assistant check
All committers have signed the CLA.

Comment thread src/acl.c Outdated
@sundb sundb added this to Redis 8.8 Mar 5, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Redis 8.8 Mar 5, 2026
@sundb sundb added the release-notes indication that this issue needs to be mentioned in the release notes label Mar 5, 2026
Comment thread src/acl.c Outdated
Comment on lines +1860 to +1863
/* Validate arity before key/channel extraction to avoid touching argv out of bounds. */
if (!commandCheckArity(cmd, argc, NULL)) {
if (idxptr) *idxptr = 0;
return ACL_DENIED_CMD;
Copy link
Copy Markdown
Collaborator

@sundb sundb Mar 9, 2026

Choose a reason for hiding this comment

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

do you mean there is also a similar issue here? but i don't see the crash whey i ran the scipting test.

make SANITIZER=address
./runtest --single unit/scripting

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah good catch! I think it's because the scripting path has a growing argv buffer so earlier test commands created a larger buffer masking ASan error. Updated the test to use a new server.

The fix in db.c alone is not enough because after the fix, getKeysUsingKeySpecs correctly returns -1, but then getKeysFromCommandWithSpecs will invoke evalGetKeys via cmd->getkeys_proc, causing oob read in genericGetKeys. So the acl.c check is actually preventing the crash. The db.c makes the argv check logic correct.

Without the fix in db.c:

==2279681==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000026ef8 at pc 0x55db5d73d7c3 bp 0x7ffc6f5dc420 sp 0x7ffc6f5dc410
READ of size 8 at 0x503000026ef8 thread T0
    #0 0x55db5d73d7c2 in getKeysUsingKeySpecs .../redis/src/db.c:3149
    #1 0x55db5d73d7c2 in getKeysUsingKeySpecs .../redis/src/db.c:3091
    #2 0x55db5d73dddc in getKeysFromCommandWithSpecs .../redis/src/db.c:3230
    #3 0x55db5dabf1db in ACLSelectorCheckCmd .../redis/src/acl.c:1726
    #4 0x55db5dac003a in ACLCheckAllUserCommandPerm .../redis/src/acl.c:1894
    #5 0x55db5d9fdd1d in RM_ACLCheckCommandPermissions .../redis/src/module.c:10425

With the fix in db.c:

==2273397==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000026ef8 at pc 0x5598e4fb8c0f bp 0x7ffc94ab48e0 sp 0x7ffc94ab48d0
READ of size 8 at 0x503000026ef8 thread T0
    #0 0x5598e4fb8c0e in genericGetKeys .../redis/src/db.c:3459
    #1 0x5598e4fb8c0e in evalGetKeys .../redis/src/db.c:3501
    #2 0x5598e4fbce64 in getKeysFromCommandWithSpecs .../redis/src/db.c:3244
    #3 0x5598e533e12b in ACLSelectorCheckCmd .../redis/src/acl.c:1726
    #4 0x5598e533ef8a in ACLCheckAllUserCommandPerm .../redis/src/acl.c:1894
    #5 0x5598e527cc6d in RM_ACLCheckCommandPermissions .../redis/src/module.c:10425

Thanks for checking!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

not sure this is the right way, my concern is that whether ethere are other paths where an invalid command could be passed into genericGetKeys().
another alternative is to add if (keyCountOfs >= argc) return 0; to genericGetKeys() before touch the argv.
@oranagra please share your thoughts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i think it's a good idea to have both.
i.e. if a command arity is wrong, abort early (not sure if ACL_DENIED_CMD is the right return value).
this will reduce many risks due to missing range checks.

but also, it's a good idea to have boundary checks like we did in the other places (which is mostly needed in case some command spec or arity is wrongly defined)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

seem that there isn't a return value better than ACL_DENIED_CMD.

/* Return values for ACLCheckAllPerm(). */
#define ACL_OK 0
#define ACL_DENIED_CMD 1
#define ACL_DENIED_KEY 2
#define ACL_DENIED_AUTH 3 /* Only used for ACL LOG entries. */
#define ACL_DENIED_CHANNEL 4 /* Only used for pub/sub commands */
#define ACL_INVALID_TLS_CERT_AUTH 5 /* Only used for TLS Auto-authentication */

@zzjas PTAL

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! I added the check in genericGetKeys() as well. Now the tests won't crash due ASan even if the acl.c fix is removed -- but still kept it as suggested for defense in depth.

@zzjas zzjas force-pushed the fix-acl-arity-check branch 2 times, most recently from b72c311 to 503d152 Compare March 11, 2026 18:36
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Comment thread src/db.c Outdated
@zzjas zzjas force-pushed the fix-acl-arity-check branch from 503d152 to 5187494 Compare March 11, 2026 18:43
@sundb
Copy link
Copy Markdown
Collaborator

sundb commented Mar 12, 2026

@zzjas please also add a similar check in extractSlotFromKeysResult()

diff --git a/src/cluster.c b/src/cluster.c
index ea0c67636..4bb2412c8 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -1130,13 +1130,16 @@ void clusterCommand(client *c) {
  *   - The slot number if all keys belong to the same slot
  *   - INVALID_CLUSTER_SLOT if there are no keys or cluster is disabled
  *   - CLUSTER_CROSSSLOT if keys belong to different slots (cross-slot error) */
-int extractSlotFromKeysResult(robj **argv, getKeysResult *keys_result) {
+int extractSlotFromKeysResult(robj **argv, int argc, getKeysResult *keys_result) {
     if (keys_result->numkeys == 0 || !server.cluster_enabled)
         return INVALID_CLUSTER_SLOT;
 
     int first_slot = INVALID_CLUSTER_SLOT;
     for (int j = 0; j < keys_result->numkeys; j++) {
-        robj *this_key = argv[keys_result->keys[j].pos];
+        int pos = keys_result->keys[j].pos;
+        if (pos >= argc || pos < 0)
+            continue;
+        robj *this_key = argv[pos];
         int this_slot = (int)keyHashSlot((char*)this_key->ptr, sdslen(this_key->ptr));
 
         if (first_slot == INVALID_CLUSTER_SLOT)

@tezc PTAL, not sure if it's needed.

@tezc
Copy link
Copy Markdown
Collaborator

tezc commented Mar 12, 2026

@sundb I'm not sure if there in an actual issue leading to extractSlotFromKeysResult() but perhaps we can have that boundary check just to be defensive?

@zzjas zzjas force-pushed the fix-acl-arity-check branch from 5187494 to f332363 Compare March 12, 2026 20:14
@zzjas
Copy link
Copy Markdown
Contributor Author

zzjas commented Mar 12, 2026

@sundb added the check in extractSlotFromKeysResult()

Comment thread src/cluster.c Outdated
@zzjas zzjas force-pushed the fix-acl-arity-check branch 2 times, most recently from b222cc9 to 468a980 Compare March 13, 2026 02:41
@ShooterIT
Copy link
Copy Markdown
Member

Returning ACL_DENIED_CMD due to wrong arity seems odd, it is not a permissions issue

Maybe we should check command arity before calling ACLCheckAllUserCommandPerm, actually we did that in most places:
processCommand
scriptCall(redis.call/pcall)
RedisModule_Call

but luaRedisAclCheckCmdPermissionsCommand (redis.acl_check_cmd) andRedisModule_ACLCheckCommandPermissions API don't, maybe we should call commandCheckArity first

Add commandCheckArity() in luaRedisAclCheckCmdPermissionsCommand and RM_ACLCheckCommandPermissions before calling ACLCheckAllUserCommandPerm, matching processCommand, scriptCall, and RM_Call. Without this, KEYNUM keyspec commands like EVAL with wrong arity cause out-of-bounds argv access during key extraction.

Also fix KEYNUM index calculation (`first + keynumidx`) and add a bounds check in genericGetKeys().
@zzjas zzjas force-pushed the fix-acl-arity-check branch from 468a980 to d3d1f31 Compare March 15, 2026 20:42
@zzjas
Copy link
Copy Markdown
Contributor Author

zzjas commented Mar 15, 2026

Returning ACL_DENIED_CMD due to wrong arity seems odd, it is not a permissions issue

Maybe we should check command arity before calling ACLCheckAllUserCommandPerm, actually we did that in most places: processCommand scriptCall(redis.call/pcall) RedisModule_Call

but luaRedisAclCheckCmdPermissionsCommand (redis.acl_check_cmd) andRedisModule_ACLCheckCommandPermissions API don't, maybe we should call commandCheckArity first

@ShooterIT I think this makes more sense then making ACLCheckAllUserCommandPerm() check the arity as well. Updated the fix and the tests. PTAL @sundb

@sundb sundb requested a review from ShooterIT March 16, 2026 01:29
Copy link
Copy Markdown
Member

@ShooterIT ShooterIT left a comment

Choose a reason for hiding this comment

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

LGTM, please update the top comment

@zzjas
Copy link
Copy Markdown
Contributor Author

zzjas commented Mar 16, 2026

@ShooterIT Thanks for the review!

please update the top comment

Just to make sure, do you mean the PR description or the comments for RM_ACLCheckCommandPermissions and luaRedisAclCheckCmdPermissionsCommand?

@ShooterIT
Copy link
Copy Markdown
Member

Validate command arity in ACL checks

now we validate command arity before ACL checks

RM_ACLCheckCommandPermissions and luaRedisAclCheckCmdPermissionsCommand?

Yes, these are user-aware APIs and should be mentioned when we modify their behavior

@zzjas
Copy link
Copy Markdown
Contributor Author

zzjas commented Mar 17, 2026

Thanks! Updated the descirption.

@sundb sundb changed the title Fix ACL checks for wrong-arity KEYNUM commands Fix ACL OOB for wrong-arity KEYNUM commands by validating arity first Mar 19, 2026
@sundb sundb changed the title Fix ACL OOB for wrong-arity KEYNUM commands by validating arity first Fix ACL OOB for wrong-arity KEYNUM commands Mar 19, 2026
@sundb sundb merged commit c4d7458 into redis:unstable Mar 19, 2026
18 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Redis 8.8 Mar 19, 2026
@sundb
Copy link
Copy Markdown
Collaborator

sundb commented Mar 19, 2026

This bug has existed for a long time, so we only backport it to the latest version.

@YaacovHazan YaacovHazan moved this from Todo to In Progress in Redis 8.6 Backport Mar 19, 2026
YaacovHazan pushed a commit to YaacovHazan/redis that referenced this pull request Mar 19, 2026
`luaRedisAclCheckCmdPermissionsCommand` and
`RM_ACLCheckCommandPermissions` now call `commandCheckArity()` to check
command arity before calling `ACLCheckAllUserCommandPerm`, matching the
behavior of `processCommand`, `scriptCall`, and `RM_Call`. Without this,
KEYNUM keyspec commands like EVAL with wrong arity cause out-of-bounds
argv access during key extraction.

Also fix KEYNUM index calculation (`first + keynumidx`) and add a bounds
check in genericGetKeys().

Add scripting and module ACL tests for wrong-arity `EVAL` to lock in the
non-crashing behavior.

Fixes redis#14843
YaacovHazan pushed a commit that referenced this pull request Mar 23, 2026
`luaRedisAclCheckCmdPermissionsCommand` and
`RM_ACLCheckCommandPermissions` now call `commandCheckArity()` to check
command arity before calling `ACLCheckAllUserCommandPerm`, matching the
behavior of `processCommand`, `scriptCall`, and `RM_Call`. Without this,
KEYNUM keyspec commands like EVAL with wrong arity cause out-of-bounds
argv access during key extraction.

Also fix KEYNUM index calculation (`first + keynumidx`) and add a bounds
check in genericGetKeys().

Add scripting and module ACL tests for wrong-arity `EVAL` to lock in the
non-crashing behavior.

Fixes #14843
@YaacovHazan YaacovHazan mentioned this pull request Mar 24, 2026
@YaacovHazan YaacovHazan moved this from In Progress to Done in Redis 8.6 Backport Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-notes indication that this issue needs to be mentioned in the release notes

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[BUG] Server crash caused by OOB read in getKeysUsingKeySpecs

7 participants