Skip to content

Commit

Permalink
chore(lua): Return which undeclared key was accessed (#3245)
Browse files Browse the repository at this point in the history
* chore(lua): Return which undeclared key was accessed

Example:

```
127.0.0.1:6379> EVAL "return redis.call('SET', 'k', 'v')" 0
(error) ERR Error running script (call to 5c4d62f4e30c54fb15935b5892148e5ce7374077): @user_script:2: script tried accessing undeclared key, key: k
```

* fix

---------

Co-authored-by: Roman Gershman <[email protected]>
  • Loading branch information
2 people authored and adiholden committed Jul 2, 2024
1 parent 20f4507 commit 2ff6282
Showing 1 changed file with 20 additions and 16 deletions.
36 changes: 20 additions & 16 deletions src/server/main_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -967,36 +967,40 @@ optional<ErrorReply> Service::CheckKeysOwnership(const CommandId* cid, CmdArgLis

// Return OK if all keys are allowed to be accessed: either declared in EVAL or
// transaction is running in global or non-atomic mode.
OpStatus CheckKeysDeclared(const ConnectionState::ScriptInfo& eval_info, const CommandId* cid,
CmdArgList args, Transaction* trans) {
optional<ErrorReply> CheckKeysDeclared(const ConnectionState::ScriptInfo& eval_info,
const CommandId* cid, CmdArgList args, Transaction* trans) {
Transaction::MultiMode multi_mode = trans->GetMultiMode();

// We either scheduled on all shards or re-schedule for each operation,
// so we are not restricted to any keys.
if (multi_mode == Transaction::GLOBAL || multi_mode == Transaction::NON_ATOMIC)
return OpStatus::OK;
return nullopt;

OpResult<KeyIndex> key_index_res = DetermineKeys(cid, args);
if (!key_index_res)
return key_index_res.status();
return ErrorReply{key_index_res.status()};

// TODO: Switch to transaction internal locked keys once single hop multi transactions are merged
// const auto& locked_keys = trans->GetMultiKeys();
const auto& locked_tags = eval_info.lock_tags;

const auto& key_index = *key_index_res;
for (unsigned i = key_index.start; i < key_index.end; ++i) {
LockTag tag{ArgS(args, i)};
string_view key = ArgS(args, i);
LockTag tag{key};
if (!locked_tags.contains(tag)) {
VLOG(1) << "Key " << string_view(tag) << " is not declared for command " << cid->name();
return OpStatus::KEY_NOTFOUND;
return ErrorReply(absl::StrCat(kUndeclaredKeyErr, ", key: ", key));
}
}

if (key_index.bonus && !locked_tags.contains(LockTag{ArgS(args, *key_index.bonus)}))
return OpStatus::KEY_NOTFOUND;
if (key_index.bonus) {
string_view key = ArgS(args, *key_index.bonus);
if (!locked_tags.contains(LockTag{key})) {
return ErrorReply(absl::StrCat(kUndeclaredKeyErr, ", key: ", key));
}
}

return OpStatus::OK;
return nullopt;
}

static optional<ErrorReply> VerifyConnectionAclStatus(const CommandId* cid,
Expand Down Expand Up @@ -1123,14 +1127,14 @@ std::optional<ErrorReply> Service::VerifyCommandState(const CommandId* cid, CmdA
}

if (under_script && cid->IsTransactional()) {
OpStatus status =
auto err =
CheckKeysDeclared(*dfly_cntx.conn_state.script_info, cid, tail_args, dfly_cntx.transaction);

if (status == OpStatus::KEY_NOTFOUND)
return ErrorReply(kUndeclaredKeyErr);

if (status != OpStatus::OK)
return ErrorReply{status};
if (err.has_value()) {
VLOG(1) << "CheckKeysDeclared failed with error " << err->ToSv() << " for command "
<< cid->name();
return err.value();
}
}

return VerifyConnectionAclStatus(cid, &dfly_cntx, "has no ACL permissions", tail_args);
Expand Down

0 comments on commit 2ff6282

Please sign in to comment.