Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/sdk/dotnet/core/Api/Sandbox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ internal Sandbox(
string? inputDir,
string? outputDir,
bool tempOutput,
FilesystemLimitsConfiguration? filesystemLimits,
SandboxBackend backend = SandboxBackend.Wasm)
{
// Pin the module path string for the FFI call duration (null for JS backend).
Expand Down Expand Up @@ -78,6 +79,17 @@ internal Sandbox(
}
}

if (filesystemLimits is { } limits)
{
var r = SafeNativeMethods.hyperlight_sandbox_set_filesystem_limits(
_handle,
(uint)limits.Mode,
limits.MaxFileSize,
limits.MaxTotalSize,
limits.MaxFileCount);
r.ThrowIfError();
}

// Apply optional configuration.
// Note: GC.KeepAlive(this) is not needed in the constructor — the
// object cannot be finalized while its constructor is still running.
Expand Down
92 changes: 92 additions & 0 deletions src/sdk/dotnet/core/Api/SandboxBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public sealed class SandboxBuilder
private string? _inputDir;
private string? _outputDir;
private bool _tempOutput;
private FilesystemLimitsConfiguration? _filesystemLimits;
private SandboxBackend _backend = SandboxBackend.Wasm;

/// <summary>
Expand Down Expand Up @@ -145,6 +146,74 @@ public SandboxBuilder WithTempOutput(bool enabled = true)
return this;
}

/// <summary>
/// Sets finite logical resource limits for the writable filesystem.
/// </summary>
/// <param name="maxFileSize">
/// Maximum logical size of one file, such as <c>"64Mi"</c>, or raw bytes
/// as a string.
/// </param>
/// <param name="maxTotalSize">
/// Maximum combined logical size of all files, such as <c>"256Mi"</c>,
/// or raw bytes as a string.
/// </param>
/// <param name="maxFileCount">Maximum number of files.</param>
/// <returns>This builder for chaining.</returns>
/// <remarks>
/// A value of zero is a real finite limit. Calling this method replaces
/// any filesystem policy previously configured on this builder.
/// </remarks>
public SandboxBuilder WithFilesystemLimits(
string maxFileSize,
string maxTotalSize,
ulong maxFileCount)
{
return WithFilesystemLimits(
SizeParser.Parse(maxFileSize),
SizeParser.Parse(maxTotalSize),
maxFileCount);
}

/// <summary>
/// Sets finite logical resource limits for the writable filesystem.
/// </summary>
/// <param name="maxFileSize">Maximum logical size of one file, in bytes.</param>
/// <param name="maxTotalSize">
/// Maximum combined logical size of all files, in bytes.
/// </param>
/// <param name="maxFileCount">Maximum number of files.</param>
/// <returns>This builder for chaining.</returns>
/// <remarks>
/// A value of zero is a real finite limit. Calling this method replaces
/// any filesystem policy previously configured on this builder.
/// </remarks>
public SandboxBuilder WithFilesystemLimits(
ulong maxFileSize,
ulong maxTotalSize,
ulong maxFileCount)
{
_filesystemLimits = FilesystemLimitsConfiguration.Finite(
maxFileSize,
maxTotalSize,
maxFileCount);
return this;
}

/// <summary>
/// Removes all logical size and file-count limits from the writable
/// filesystem.
/// </summary>
/// <returns>This builder for chaining.</returns>
/// <remarks>
/// Calling this method replaces any filesystem policy previously
/// configured on this builder.
/// </remarks>
public SandboxBuilder WithUnlimitedFilesystemLimits()
{
_filesystemLimits = FilesystemLimitsConfiguration.Unlimited;
return this;
}

/// <summary>
/// Creates a new <see cref="Sandbox"/> with the configured settings.
/// </summary>
Expand Down Expand Up @@ -176,6 +245,29 @@ public Sandbox Build()
_inputDir,
_outputDir,
_tempOutput,
_filesystemLimits,
_backend);
}
}

internal readonly record struct FilesystemLimitsConfiguration(
FilesystemLimitsMode Mode,
ulong MaxFileSize,
ulong MaxTotalSize,
ulong MaxFileCount)
{
internal static FilesystemLimitsConfiguration Finite(
ulong maxFileSize,
ulong maxTotalSize,
ulong maxFileCount)
=> new(FilesystemLimitsMode.Finite, maxFileSize, maxTotalSize, maxFileCount);

internal static FilesystemLimitsConfiguration Unlimited =>
new(FilesystemLimitsMode.Unlimited, 0, 0, 0);
}

internal enum FilesystemLimitsMode : uint
{
Finite = 0,
Unlimited = 1,
}
13 changes: 13 additions & 0 deletions src/sdk/dotnet/core/PInvoke/SafeNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ internal static partial FFIResult hyperlight_sandbox_set_temp_output(
SandboxSafeHandle handle,
[MarshalAs(UnmanagedType.I1)] bool enabled);

/// <summary>
/// Sets the writable filesystem quota policy.
/// Mode 0 is finite and mode 1 is unlimited.
/// </summary>
[LibraryImport(LibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static partial FFIResult hyperlight_sandbox_set_filesystem_limits(
SandboxSafeHandle handle,
uint mode,
ulong maxFileSize,
ulong maxTotalSize,
ulong maxFileCount);

/// <summary>Adds a domain to the network allowlist.</summary>
[LibraryImport(LibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,45 @@ public void FreeSandbox_Null_DoesNotCrash()
{
SafeNativeMethods.hyperlight_sandbox_free(IntPtr.Zero);
}

[Fact]
public void SetFilesystemLimits_ZeroFiniteValues_Succeeds()
{
using var handle = CreateSandboxHandle();
var result = SafeNativeMethods.hyperlight_sandbox_set_filesystem_limits(
handle, 0, 0, 0, 0);

result.ThrowIfError();
}

[Fact]
public void SetFilesystemLimits_InvalidMode_ThrowsArgumentException()
{
using var handle = CreateSandboxHandle();
var result = SafeNativeMethods.hyperlight_sandbox_set_filesystem_limits(
handle, 2, 0, 0, 0);

Assert.Throws<ArgumentException>(() => result.ThrowIfError());
}

private static SandboxSafeHandle CreateSandboxHandle()
{
var modulePath = Marshal.StringToCoTaskMemUTF8("/tmp/test.wasm");
try
{
var result = SafeNativeMethods.hyperlight_sandbox_create(new FFISandboxOptions
{
module_path = modulePath,
heap_size = 0,
stack_size = 0,
backend = 0,
});
result.ThrowIfError();
return new SandboxSafeHandle(result.value);
}
finally
{
Marshal.FreeCoTaskMem(modulePath);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,52 @@ public void WithTempOutput_Works()
Assert.NotNull(sandbox);
}

[Fact]
public void WithFilesystemLimits_StringValues_Works()
{
using var sandbox = new SandboxBuilder()
.WithModulePath("/tmp/test.wasm")
.WithFilesystemLimits("64Mi", "256Mi", 1024)
.Build();

Assert.NotNull(sandbox);
}

[Fact]
public void WithFilesystemLimits_ZeroValues_AreFiniteLimits()
{
using var sandbox = new SandboxBuilder()
.WithModulePath("/tmp/test.wasm")
.WithFilesystemLimits(0, 0, 0)
.Build();

Assert.NotNull(sandbox);
}

[Fact]
public void WithUnlimitedFilesystemLimits_Works()
{
using var sandbox = new SandboxBuilder()
.WithModulePath("/tmp/test.wasm")
.WithUnlimitedFilesystemLimits()
.Build();

Assert.NotNull(sandbox);
}

[Fact]
public void FilesystemPolicy_LastCallReplacesPriorPolicy()
{
using var sandbox = new SandboxBuilder()
.WithModulePath("/tmp/test.wasm")
.WithFilesystemLimits(1, 2, 3)
.WithUnlimitedFilesystemLimits()
.WithFilesystemLimits(4, 5, 6)
.Build();
Comment thread
jsturtevant marked this conversation as resolved.

Assert.NotNull(sandbox);
}

[Fact]
public void ChainedConfiguration_AllOptions_Works()
{
Expand Down
Loading
Loading