Skip to content

Commit

Permalink
Merge pull request #21 from axone-protocol/security/forbid-halt
Browse files Browse the repository at this point in the history
Security/forbid halt
  • Loading branch information
ccamel authored Nov 24, 2024
2 parents d446d5b + 3a63e6e commit eec929d
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 20 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The following customizations have been made to adapt the original `ichiban/prolo
- Removed support for trigonometric functions (`sin`, `cos`, `tan`, `asin`, `acos`, `atan`).
- Introduced VM hooks for enhanced Prolog execution control.
- Added support for the `Dict` term.
- `halt/0` and `halt/1` are forbidden and will throw an error.

## License

Expand Down
4 changes: 3 additions & 1 deletion engine/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1960,7 +1960,9 @@ func PeekChar(vm *VM, streamOrAlias, char Term, k Cont, env *Env) *Promise {
}
}

var osExit = os.Exit
var osExit = func(_ int) {
panic("halt/1 is not allowed")
}

// Halt exits the process with exit code of n.
func Halt(_ *VM, n Term, k Cont, env *Env) *Promise {
Expand Down
33 changes: 16 additions & 17 deletions engine/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func TestCall(t *testing.T) {
vm.Register0(NewAtom("do_not_call_wrapped"), func(*VM, Cont, *Env) *Promise {
panic(errors.New("told you"))
})
vm.Register0(NewAtom("do_not_call_exception"), func(*VM, Cont, *Env) *Promise {
panic(Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("told you")))})
})
vm.Register0(NewAtom("do_not_call_misc_error"), func(*VM, Cont, *Env) *Promise {
panic(42)
})
assert.NoError(t, vm.Compile(context.Background(), `
foo.
foo(_, _).
Expand Down Expand Up @@ -64,9 +70,11 @@ f(g([a, [b, c|X], Y{x:5}])).

{title: `cover all`, goal: atomComma.Apply(atomCut, NewAtom("f").Apply(NewAtom("g").Apply(List(NewAtom("a"), PartialList(NewVariable(), NewAtom("b"), NewAtom("c")), makeDict(NewAtom("foo"), NewAtom("x"), Integer(5)))))), ok: true},
{title: `out of memory`, goal: NewAtom("foo").Apply(NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable()), err: resourceError(resourceMemory, nil), mem: 1},
{title: `panic`, goal: NewAtom("do_not_call"), err: PanicError{errors.New("told you")}},
{title: `panic (lazy)`, goal: NewAtom("lazy_do_not_call"), err: PanicError{errors.New("told you")}},
{title: `panic (wrapped)`, goal: NewAtom("do_not_call_wrapped"), err: PanicError{errors.New("told you")}},
{title: `panic`, goal: NewAtom("do_not_call"), err: Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("told you")))}},
{title: `panic (lazy)`, goal: NewAtom("lazy_do_not_call"), err: Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("told you")))}},
{title: `panic (wrapped)`, goal: NewAtom("do_not_call_wrapped"), err: Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("told you")))}},
{title: `panic (exception)`, goal: NewAtom("do_not_call_exception"), err: Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("told you")))}},
{title: `panic (misc)`, goal: NewAtom("do_not_call_misc_error"), err: Exception{NewAtom("error").Apply(NewAtom("panic_error").Apply(NewAtom("42")))}},
}

for _, tt := range tests {
Expand Down Expand Up @@ -5519,20 +5527,11 @@ func TestPeekChar(t *testing.T) {

func Test_Halt(t *testing.T) {
t.Run("ok", func(t *testing.T) {
var exitCalled bool
osExit = func(code int) {
assert.Equal(t, 2, code)
exitCalled = true
}
defer func() {
osExit = os.Exit
}()

ok, err := Halt(nil, Integer(2), Success, nil).Force(context.Background())
assert.NoError(t, err)
assert.True(t, ok)

assert.True(t, exitCalled)
ok, err := Delay(func(ctx context.Context) *Promise {
return Halt(nil, Integer(2), Success, nil)
}).Force(context.Background())
assert.EqualError(t, err, "error(panic_error(halt/1 is not allowed))")
assert.False(t, ok)
})

t.Run("n is a variable", func(t *testing.T) {
Expand Down
6 changes: 4 additions & 2 deletions engine/promise.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,12 @@ func ensurePromise(p **Promise) {

func panicError(r interface{}) error {
switch r := r.(type) {
case Exception:
return r
case error:
return PanicError{r}
return Exception{term: atomError.Apply(NewAtom("panic_error").Apply(NewAtom(r.Error())))}
default:
return PanicError{fmt.Errorf("%v", r)}
return Exception{term: atomError.Apply(NewAtom("panic_error").Apply(NewAtom(fmt.Sprintf("%v", r))))}
}
}

Expand Down

0 comments on commit eec929d

Please sign in to comment.