Skip to content

Commit b5135c6

Browse files
Implement remaining Lua runtime libraries (#1075)
* punch an allow list in for Lua functions * implement lua function allow listing * test to make sure we're exporting exactly what Redis does by default, and we can configure it reasonably; implements a number of workarounds to accomplish this, as the 5.1 <-> 5.4 Lua version difference also had some library changes * implement cjson.encode and decode * implement bit.xxx operations in Lua scripts; the rules around these are pretty sloppy, so ran tests against Redis first to verify matching behavior * fix typo in AllowedFunctions * implement message pack support (cmsgpack.pack and cmsgpack.unpack) * implement struct.pack/unpack/size; these are provided by Lua and are a superset of what Redis wants; technically we allow more than Redis does, but I think that's a reasonable tradeoff for implementation complexity * allow redis functions to be allowed/denied rather than unconditionally included * fix formatting * Move intermediate binary blobs onto ScratchBufferManager * formatting * strange this doesn't error locally; removing preview use of params ReadOnlySpan<T> * test for #1079, fails in main and passes here * tests (and fixes) for math.frexp and math.ldexp * tests for table.maxn and loadstring * prevent LuaAllowedFunctions from importing functions not on the default import list * enforce maximum depth for cjson functions * enforce maximum encoding depth for cmsgpack.pack * hey, a reliable-ish repro for the linux longjmp issue; disable these exception tests on Linux until that refactor happens * condition more exception tests on 'we're on Windows' until the refactor comes in * knock out a missing TODO; remove List<byte> and use scratch buffer instead * Update test/Garnet.test/LuaScriptRunnerTests.cs Co-authored-by: Copilot <[email protected]> * add more conditions to skip exceptions in .NET 9 or non-Windows builds; consolidate condition checks; restore non-error-y parts of tests in .NET 9 and non-Windows builds --------- Co-authored-by: Copilot <[email protected]>
1 parent 25f10dc commit b5135c6

File tree

14 files changed

+3895
-296
lines changed

14 files changed

+3895
-296
lines changed

benchmark/BDN.benchmark/Lua/LuaParams.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public LuaParams(LuaMemoryManagementMode mode, bool memoryLimit, TimeSpan? timeo
2929
/// Get the equivalent <see cref="LuaOptions"/>.
3030
/// </summary>
3131
public LuaOptions CreateOptions()
32-
=> new(Mode, MemoryLimit ? "2m" : "", Timeout ?? System.Threading.Timeout.InfiniteTimeSpan, LuaLoggingMode.Enable);
32+
=> new(Mode, MemoryLimit ? "2m" : "", Timeout ?? System.Threading.Timeout.InfiniteTimeSpan, LuaLoggingMode.Enable, []);
3333

3434
/// <summary>
3535
/// String representation

benchmark/BDN.benchmark/Operations/OperationsBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public virtual void GlobalSetup()
5656
QuietMode = true,
5757
EnableLua = true,
5858
DisablePubSub = true,
59-
LuaOptions = new(LuaMemoryManagementMode.Native, "", Timeout.InfiniteTimeSpan, LuaLoggingMode.Enable),
59+
LuaOptions = new(LuaMemoryManagementMode.Native, "", Timeout.InfiniteTimeSpan, LuaLoggingMode.Enable, []),
6060
};
6161

6262
if (Params.useAof)

libs/host/Configuration/Options.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,27 @@ internal sealed class Options
560560
[Option("lua-logging-mode", Required = false, HelpText = "Behavior of redis.log(...) when called from Lua scripts. Defaults to Enable.")]
561561
public LuaLoggingMode LuaLoggingMode { get; set; }
562562

563+
// Parsing is a tad tricky here as JSON wants to set to empty at certain points
564+
//
565+
// A bespoke union-on-set gets the desired semantics.
566+
private readonly HashSet<string> luaAllowedFunctions = [];
567+
568+
[OptionValidation]
569+
[Option("lua-allowed-functions", Separator = ',', Required = false, HelpText = "If set, restricts the functions available in Lua scripts to given list.")]
570+
public IEnumerable<string> LuaAllowedFunctions
571+
{
572+
get => luaAllowedFunctions;
573+
set
574+
{
575+
if (value == null)
576+
{
577+
return;
578+
}
579+
580+
luaAllowedFunctions.UnionWith(value);
581+
}
582+
}
583+
563584
[FilePathValidation(false, true, false)]
564585
[Option("unixsocket", Required = false, HelpText = "Unix socket address path to bind server to")]
565586
public string UnixSocketPath { get; set; }
@@ -811,7 +832,7 @@ public GarnetServerOptions GetServerOptions(ILogger logger = null)
811832
LoadModuleCS = LoadModuleCS,
812833
FailOnRecoveryError = FailOnRecoveryError.GetValueOrDefault(),
813834
SkipRDBRestoreChecksumValidation = SkipRDBRestoreChecksumValidation.GetValueOrDefault(),
814-
LuaOptions = EnableLua.GetValueOrDefault() ? new LuaOptions(LuaMemoryManagementMode, LuaScriptMemoryLimit, LuaScriptTimeoutMs == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(LuaScriptTimeoutMs), LuaLoggingMode, logger) : null,
835+
LuaOptions = EnableLua.GetValueOrDefault() ? new LuaOptions(LuaMemoryManagementMode, LuaScriptMemoryLimit, LuaScriptTimeoutMs == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(LuaScriptTimeoutMs), LuaLoggingMode, LuaAllowedFunctions, logger) : null,
815836
UnixSocketPath = UnixSocketPath,
816837
UnixSocketPermission = unixSocketPermissions
817838
};

libs/host/defaults.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@
380380
/* Allow redis.log(...) to write to the Garnet logs */
381381
"LuaLoggingMode": "Enable",
382382

383+
/* Allow all built in and redis.* functions by default */
384+
"LuaAllowedFunctions": null,
385+
383386
/* Unix socket address path to bind the server to */
384387
"UnixSocketPath": null,
385388

libs/server/ArgSlice/ScratchBufferManager.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,27 @@ public ArgSlice CreateArgSlice(string str)
114114
return retVal;
115115
}
116116

117+
public ReadOnlySpan<byte> UTF8EncodeString(string str)
118+
{
119+
// We'll always need AT LEAST this many bytes
120+
ExpandScratchBufferIfNeeded(str.Length);
121+
122+
var space = FullBuffer()[scratchBufferOffset..];
123+
124+
// Attempt to fit in the existing buffer first
125+
if (!Encoding.UTF8.TryGetBytes(str, space, out var written))
126+
{
127+
// If that fails, figure out exactly how much space we need
128+
var neededBytes = Encoding.UTF8.GetByteCount(str);
129+
ExpandScratchBufferIfNeeded(neededBytes);
130+
131+
space = FullBuffer()[scratchBufferOffset..];
132+
written = Encoding.UTF8.GetBytes(str, space);
133+
}
134+
135+
return space[..written];
136+
}
137+
117138
/// <summary>
118139
/// Create an ArgSlice that includes a header of specified size, followed by RESP Bulk-String formatted versions of the specified ArgSlice values (arg1 and arg2)
119140
/// </summary>

libs/server/Lua/LuaOptions.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Runtime.InteropServices;
67
using Microsoft.Extensions.Logging;
78

@@ -18,6 +19,7 @@ public sealed class LuaOptions
1819
public string MemoryLimit = "";
1920
public TimeSpan Timeout = System.Threading.Timeout.InfiniteTimeSpan;
2021
public LuaLoggingMode LogMode = LuaLoggingMode.Silent;
22+
public HashSet<string> AllowedFunctions = [];
2123

2224
/// <summary>
2325
/// Construct options with default options.
@@ -30,12 +32,13 @@ public LuaOptions(ILogger logger = null)
3032
/// <summary>
3133
/// Construct options with specific settings.
3234
/// </summary>
33-
public LuaOptions(LuaMemoryManagementMode memoryMode, string memoryLimit, TimeSpan timeout, LuaLoggingMode logMode, ILogger logger = null) : this(logger)
35+
public LuaOptions(LuaMemoryManagementMode memoryMode, string memoryLimit, TimeSpan timeout, LuaLoggingMode logMode, IEnumerable<string> allowedFunctions, ILogger logger = null) : this(logger)
3436
{
3537
MemoryManagementMode = memoryMode;
3638
MemoryLimit = memoryLimit;
3739
Timeout = timeout;
3840
LogMode = logMode;
41+
AllowedFunctions = new HashSet<string>(allowedFunctions, StringComparer.OrdinalIgnoreCase);
3942
}
4043

4144
/// <summary>

0 commit comments

Comments
 (0)