Skip to content

Commit

Permalink
feat(sandboxing): -Dcycle_limit
Browse files Browse the repository at this point in the history
closes #182
  • Loading branch information
giann committed Jan 19, 2024
1 parent 3a7281a commit 14e5dce
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Added
- REPL (https://github.com/buzz-language/buzz/issues/17) available by running buzz without any argument
- Function argument names and object property names can be ommitted if the provided value is a named variable with the same name (https://github.com/buzz-language/buzz/issues/204)
- Sandboxing build options `memory_limit` and `cycle_limit` (https://github.com/buzz-language/buzz/issues/182)

## Changed
- Map type notation has changed from `{K, V}` to `{K: V}`. Similarly map expression with specified typed went from `{<K, V>, ...}` to `{<K: V>, ...}` (https://github.com/buzz-language/buzz/issues/253)
Expand Down
9 changes: 8 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ const BuzzBuildOptions = struct {
gc: BuzzGCOptions,
jit: BuzzJITOptions,
target: Build.ResolvedTarget,
cycle_limit: ?u128,

pub fn step(self: @This(), b: *Build) *Build.Module {
var options = b.addOptions();
options.addOption(@TypeOf(self.version), "version", self.version);
options.addOption(@TypeOf(self.sha), "sha", self.sha);
options.addOption(@TypeOf(self.mimalloc), "mimalloc", self.mimalloc);
options.addOption(@TypeOf(self.cycle_limit), "cycle_limit", self.cycle_limit);

self.debug.step(options);
self.gc.step(options);
Expand Down Expand Up @@ -140,6 +142,11 @@ pub fn build(b: *Build) !void {
}).stdout,
"\n \t",
),
.cycle_limit = b.option(
usize,
"cycle_limit",
"Amount of bytecode (x 1000) the script is allowed to run (WARNING: this disables JIT compilation)",
) orelse null,
.mimalloc = b.option(
bool,
"mimalloc",
Expand Down Expand Up @@ -216,7 +223,7 @@ pub fn build(b: *Build) !void {
.memory_limit = b.option(
usize,
"memory_limit",
"Memory limit",
"Memory limit in bytes",
) orelse null,
},
.jit = .{
Expand Down
2 changes: 1 addition & 1 deletion src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7081,7 +7081,7 @@ fn importStatement(self: *Self) Error!Ast.Node.Index {
}

fn zdefStatement(self: *Self) Error!Ast.Node.Index {
if (!BuildOptions.jit) {
if (!BuildOptions.jit and BuildOptions.cycle_limit == null) {
self.reportError(.zdef, "zdef can't be used, this instance of buzz was built with JIT compiler disabled");
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor:
};
var imports = std.StringHashMap(Parser.ScriptImport).init(allocator);
var vm = try VM.init(&gc, &import_registry, flavor);
vm.jit = if (BuildOptions.jit)
vm.jit = if (BuildOptions.jit and BuildOptions.cycle_limit == null)
JIT.init(&vm)
else
null;
Expand Down
16 changes: 13 additions & 3 deletions src/repl.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn printBanner(out: std.fs.File.Writer, full: bool) void {

if (full) {
out.print(
"Built with Zig {} {s}\nAllocator: {s}\nJIT: {s}\n",
"Built with Zig {} {s}\nAllocator: {s}, Memory limit: {} {s}\nJIT: {s}, CPU limit: {} {s}\n",
.{
builtin.zig_version,
switch (builtin.mode) {
Expand All @@ -59,10 +59,20 @@ pub fn printBanner(out: std.fs.File.Writer, full: bool) void {
if (builtin.mode == .Debug)
"gpa"
else if (BuildOptions.mimalloc) "mimalloc" else "c_allocator",
if (BuildOptions.jit)
if (BuildOptions.memory_limit) |ml|
ml
else
0,
if (BuildOptions.memory_limit != null)
"bytes"
else
"(unlimited)",
if (BuildOptions.jit and BuildOptions.cycle_limit == null)
"on"
else
"off",
if (BuildOptions.cycle_limit) |cl| cl else 0,
if (BuildOptions.cycle_limit != null) "cycles" else "(unlimited)",
},
) catch unreachable;
}
Expand All @@ -83,7 +93,7 @@ pub fn repl(allocator: std.mem.Allocator) !void {
};
var imports = std.StringHashMap(Parser.ScriptImport).init(allocator);
var vm = try VM.init(&gc, &import_registry, .Repl);
vm.jit = if (BuildOptions.jit)
vm.jit = if (BuildOptions.jit and BuildOptions.cycle_limit == null)
JIT.init(&vm)
else
null;
Expand Down
24 changes: 21 additions & 3 deletions src/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ pub const Fiber = struct {
pub const VM = struct {
const Self = @This();

var cycles: u128 = 0;

pub const Error = error{
UnwrappedNull,
OutOfBound,
Expand All @@ -330,6 +332,7 @@ pub const VM = struct {
FiberOver,
BadNumber,
ReachedMaximumMemoryUsage,
ReachedMaximumCPUUsage,
Custom, // TODO: remove when user can use this set directly in buzz code
} || Allocator.Error || std.fmt.BufPrintError;

Expand Down Expand Up @@ -723,6 +726,21 @@ pub const VM = struct {
self.gc.where = current_frame.closure.function.chunk.lines.items[current_frame.ip - 1];
}

if (BuildOptions.cycle_limit) |limit| {
cycles += 1;

if (cycles > limit * 1000) {
self.throw(
Error.ReachedMaximumCPUUsage,
(self.gc.copyString("Maximum CPU usage reached") catch @panic("Maximum CPU usage reached")).toValue(),
null,
null,
) catch @panic("Maximum CPU usage reached");

return;
}
}

// Tail call
@call(
.always_tail,
Expand Down Expand Up @@ -3928,7 +3946,7 @@ pub const VM = struct {
}

// A JIT compiled function pops its stack on its own
fn callCompiled(self: *Self, closure: ?*ObjClosure, native: NativeFn, arg_count: u8, catch_value: ?Value) !void {
fn callCompiled(self: *Self, closure: *ObjClosure, native: NativeFn, arg_count: u8, catch_value: ?Value) !void {
const was_in_native_call = self.currentFrame() != null and self.currentFrame().?.in_native_call;
if (self.currentFrame()) |frame| {
frame.in_native_call = true;
Expand All @@ -3937,8 +3955,8 @@ pub const VM = struct {

var ctx = NativeCtx{
.vm = self,
.globals = if (closure) |uclosure| uclosure.globals.items.ptr else &[_]Value{},
.upvalues = if (closure) |uclosure| uclosure.upvalues.items.ptr else &[_]*ObjUpValue{},
.globals = closure.globals.items.ptr,
.upvalues = closure.upvalues.items.ptr,
.base = self.current_fiber.stack_top - arg_count - 1,
.stack_top = &self.current_fiber.stack_top,
};
Expand Down

0 comments on commit 14e5dce

Please sign in to comment.