Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: restrict actor exit codes to non-system codes #3656

Closed
wants to merge 3 commits into from
Closed
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
11 changes: 11 additions & 0 deletions chain/actors/aerrors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@ type internalActorError interface {
Unwrap() error
}

type guard struct{}

type ActorError interface {
error
IsFatal() bool
RetCode() exitcode.ExitCode
// _internal is a sentinel to prevent code outside of the aerrors package
// from implementing the ActorError interface. It ensures actors use
// Runtime.Abortf and do not panic with an ActorError directly and
// illegally using a system error code.
_internal() guard
}

type actorError struct {
Expand Down Expand Up @@ -66,4 +73,8 @@ func (e *actorError) Unwrap() error {
return e.err
}

func (e *actorError) _internal() guard {
return guard{}
}

var _ internalActorError = (*actorError)(nil)
6 changes: 3 additions & 3 deletions chain/vm/invoker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ func (b basicContract) Exports() []interface{} {
}

func (basicContract) InvokeSomething0(rt runtime.Runtime, params *basicParams) *abi.EmptyValue {
rt.Abortf(exitcode.ExitCode(params.B), "params.B")
vmabortf(exitcode.ExitCode(params.B), "params.B")
return nil
}

func (basicContract) BadParam(rt runtime.Runtime, params *basicParams) *abi.EmptyValue {
rt.Abortf(255, "bad params")
vmabortf(255, "bad params")
return nil
}

func (basicContract) InvokeSomething10(rt runtime.Runtime, params *basicParams) *abi.EmptyValue {
rt.Abortf(exitcode.ExitCode(params.B+10), "params.B")
vmabortf(exitcode.ExitCode(params.B+10), "params.B")
return nil
}

Expand Down
40 changes: 26 additions & 14 deletions chain/vm/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (rt *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.Act
ret := f()

if !rt.callerValidated {
rt.Abortf(exitcode.SysErrorIllegalActor, "Caller MUST be validated during method execution")
vmabortf(exitcode.SysErrorIllegalActor, "Caller MUST be validated during method execution")
}

switch ret := ret.(type) {
Expand Down Expand Up @@ -160,7 +160,7 @@ func (rt *Runtime) ValidateImmediateCallerAcceptAny() {
func (rt *Runtime) CurrentBalance() abi.TokenAmount {
b, err := rt.GetBalance(rt.Receiver())
if err != nil {
rt.Abortf(err.RetCode(), "get current balance: %v", err)
vmabortf(err.RetCode(), "get current balance: %v", err)
nonsense marked this conversation as resolved.
Show resolved Hide resolved
}
return b
}
Expand Down Expand Up @@ -218,16 +218,16 @@ func (rt *Runtime) NewActorAddress() address.Address {

func (rt *Runtime) CreateActor(codeID cid.Cid, address address.Address) {
if !builtin.IsBuiltinActor(codeID) {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Can only create built-in actors.")
vmabortf(exitcode.SysErrorIllegalArgument, "Can only create built-in actors.")
}

if builtin.IsSingletonActor(codeID) {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Can only have one instance of singleton actors.")
vmabortf(exitcode.SysErrorIllegalArgument, "Can only have one instance of singleton actors.")
}

_, err := rt.state.GetActor(address)
if err == nil {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Actor address already exists")
vmabortf(exitcode.SysErrorIllegalArgument, "Actor address already exists")
}

rt.chargeGas(rt.Pricelist().OnCreateActor())
Expand All @@ -253,7 +253,7 @@ func (rt *Runtime) DeleteActor(beneficiary address.Address) {
act, err := rt.state.GetActor(rt.Receiver())
if err != nil {
if xerrors.Is(err, types.ErrActorNotFound) {
rt.Abortf(exitcode.SysErrorIllegalActor, "failed to load actor in delete actor: %s", err)
vmabortf(exitcode.SysErrorIllegalActor, "failed to load actor in delete actor: %s", err)
}
panic(aerrors.Fatalf("failed to get actor: %s", err))
}
Expand Down Expand Up @@ -284,15 +284,27 @@ func (rt *Runtime) ValidateImmediateCallerIs(as ...address.Address) {
return
}
}
rt.Abortf(exitcode.SysErrForbidden, "caller %s is not one of %s", rt.Caller(), as)
vmabortf(exitcode.SysErrForbidden, "caller %s is not one of %s", rt.Caller(), as)
}

func (rt *Runtime) Context() context.Context {
return rt.ctx
}

// Abortf should be called by ACTORS to abort execution with the given non-system
// and non-negative exit code.
func (rt *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
log.Warnf("Abortf: " + fmt.Sprintf(msg, args...))
if code < exitcode.FirstActorErrorCode {
panic(aerrors.NewfSkip(2, exitcode.SysErrorIllegalActor, "Actor used illegal exit code: %s", code))
}
panic(aerrors.NewfSkip(2, code, msg, args...))
}

// vmabortf should be called by the runtime/vm to abort execution with ANY exit
// code, including system codes.
func vmabortf(code exitcode.ExitCode, msg string, args ...interface{}) {
log.Warnf("vmabortf: " + fmt.Sprintf(msg, args...))
panic(aerrors.NewfSkip(2, code, msg, args...))
}

Expand All @@ -311,7 +323,7 @@ func (rt *Runtime) ValidateImmediateCallerType(ts ...cid.Cid) {
return
}
}
rt.Abortf(exitcode.SysErrForbidden, "caller cid type %q was not one of %v", callerCid, ts)
vmabortf(exitcode.SysErrForbidden, "caller cid type %q was not one of %v", callerCid, ts)
}

func (rt *Runtime) CurrEpoch() abi.ChainEpoch {
Expand All @@ -328,13 +340,13 @@ func (dwt *dumbWrapperType) Into(um cbor.Unmarshaler) error {

func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m cbor.Marshaler, value abi.TokenAmount, out cbor.Er) exitcode.ExitCode {
if !rt.allowInternal {
rt.Abortf(exitcode.SysErrorIllegalActor, "runtime.Send() is currently disallowed")
vmabortf(exitcode.SysErrorIllegalActor, "runtime.Send() is currently disallowed")
}
var params []byte
if m != nil {
buf := new(bytes.Buffer)
if err := m.MarshalCBOR(buf); err != nil {
rt.Abortf(exitcode.ErrSerialization, "failed to marshal input parameters: %s", err)
vmabortf(exitcode.ErrSerialization, "failed to marshal input parameters: %s", err)
}
params = buf.Bytes()
}
Expand Down Expand Up @@ -407,19 +419,19 @@ func (rt *Runtime) StateCreate(obj cbor.Marshaler) {
func (rt *Runtime) StateReadonly(obj cbor.Unmarshaler) {
act, err := rt.state.GetActor(rt.Receiver())
if err != nil {
rt.Abortf(exitcode.SysErrorIllegalArgument, "failed to get actor for Readonly state: %s", err)
vmabortf(exitcode.SysErrorIllegalArgument, "failed to get actor for Readonly state: %s", err)
}
rt.StoreGet(act.Head, obj)
}

func (rt *Runtime) StateTransaction(obj cbor.Er, f func()) {
if obj == nil {
rt.Abortf(exitcode.SysErrorIllegalActor, "Must not pass nil to Transaction()")
vmabortf(exitcode.SysErrorIllegalActor, "Must not pass nil to Transaction()")
}

act, err := rt.state.GetActor(rt.Receiver())
if err != nil {
rt.Abortf(exitcode.SysErrorIllegalActor, "failed to get actor for Transaction: %s", err)
vmabortf(exitcode.SysErrorIllegalActor, "failed to get actor for Transaction: %s", err)
}
baseState := act.Head
rt.StoreGet(baseState, obj)
Expand Down Expand Up @@ -551,7 +563,7 @@ func (rt *Runtime) incrementNumActorsCreated() {

func (rt *Runtime) abortIfAlreadyValidated() {
if rt.callerValidated {
rt.Abortf(exitcode.SysErrorIllegalActor, "Method must validate caller identity exactly once")
vmabortf(exitcode.SysErrorIllegalActor, "Method must validate caller identity exactly once")
}
rt.callerValidated = true
}
Expand Down
2 changes: 1 addition & 1 deletion chain/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
vmm := *msg
resF, ok := rt.ResolveAddress(msg.From)
if !ok {
rt.Abortf(exitcode.SysErrInvalidReceiver, "resolve msg.From address failed")
vmabortf(exitcode.SysErrInvalidReceiver, "resolve msg.From address failed")
}
vmm.From = resF
rt.Message = vmm
Expand Down