Fix server crash on FUNCTION DELETE after case-only function name collision#3925
Conversation
…lision The per-library function dict (libraryFunctionDictType) compared names case-sensitively while the global function dict (functionDictType) compares them case-insensitively. Registering two functions in one library whose names differ only in case (e.g. "aaa" and "AAA") stored both in the per-library dict, but the second insert into the global dict silently failed as a duplicate. On FUNCTION DELETE (and FUNCTION LOAD REPLACE, which unlinks the old library internally), libraryUnlink iterates the per-library dict and deletes each function from the global dict. The second delete resolved to the already-removed case-insensitive slot, so dictDelete returned DICT_ERR and serverAssert(ret == DICT_OK) aborted the server. This is reachable by any client with the SCRIPTING ACL category, which is granted by default, with no special privileges. Make the per-library dict case-insensitive to match the global dict. Names differing only in case now collide at registration and FUNCTION LOAD is rejected cleanly with "Function already exists in the library", consistent with the existing case-insensitive FCALL contract. Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR updates the per-library function dictionary to use case-insensitive hashing and key comparison, ensuring consistency with the global function dictionary's name collapsing. Two test cases verify the rejection of case-collision registrations and confirm server stability after deletion attempts. ChangesFunction name case-insensitivity consistency
🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## unstable #3925 +/- ##
============================================
+ Coverage 76.63% 76.74% +0.10%
============================================
Files 162 162
Lines 80735 80735
============================================
+ Hits 61875 61958 +83
+ Misses 18860 18777 -83
🚀 New features to boost your workflow:
|
hpatro
left a comment
There was a problem hiding this comment.
Looks good to me. Shall we backport it to all the existing versions?
Problem
The per-library function dict (
libraryFunctionDictType) compares function names case-sensitively, while the global function dict (functionDictType) compares them case-insensitively (dictCStrCaseHash+dictSdsKeyCaseCompare). This asymmetry lets a single library register two functions whose names differ only in case, leaving the two dicts inconsistent and crashing the server on the next unlink.Reproducer (any client with the default
SCRIPTINGACL category)The server aborts with:
Mechanism
FUNCTION LOAD—functionLibCreateFunctionchecks for duplicates in the per-library dict case-sensitively, soaaaandAAAare both stored there.libraryLinkthen inserts both into the global dict, but the seconddictAddsilently returnsDICT_ERR(return value ignored) because the global dict is case-insensitive. The load-path global-collision check iterates the per-library functions and looks each up in the global dict, but sinceAAAwas never inserted there it finds no conflict and the load succeeds.FUNCTION DELETE—libraryUnlinkiterates the per-library dict (2 entries) and deletes each from the global dict. The first delete (aaa) succeeds; the second (AAA) resolves to the same case-insensitive slot, already removed, sodictDeletereturnsDICT_ERRandserverAssert(ret == DICT_OK)atfunctions.c:332aborts the server.FUNCTION LOAD REPLACEof an affected library hits the same path, since it callslibraryUnlinkon the old library internally.Requires only the
SCRIPTINGACL category, granted to users by default. No admin, cluster, or special config needed.Fix
Make
libraryFunctionDictTypecase-insensitive (dictCStrCaseHash+dictSdsKeyCaseCompare) to match the global dict. Names differing only in case now collide at registration, andFUNCTION LOADis rejected cleanly withFunction already exists in the library. This is consistent with the long-standing case-insensitiveFCALLcontract (see the existingFUNCTION - test function case insensitivetest).Testing
unstablebefore the fix; confirmed the server stays up andFUNCTION LOADis rejected after.tests/unit/functions.tcl. Verified they fail on pre-fix code (load wrongly succeeds, delete crashes the server) and pass after the fix.unit/functions: 122 passed, 0 failed.unit/scripting: 683 passed, 0 failed.