Skip to content

Commit 59acb38

Browse files
petar-dambovalievalbttx
authored andcommitted
fix: invoke user recover with implicit panics (#3067)
Currently only explicit panic invocations are recovered in the user code. This PR covers the implicit panics that happen because of invalid operations. Associated [issue](#1148) This maintains the distinction between VM panics and user panics. Here is a list of possible runtime panics that we will cover. - [x] Out-of-Bounds Slice or Array Access - [x] Invalid Slice Indexing - [x] Division by Zero and MOD zero - [x] Type Assertion Failure - [x] Invalid Memory Allocation (bad call to make()) - [x] Out-of-Bounds String Indexing - [x] nil pointer dereference - [x] Write to a nil map Also, fixed a small bug with the builtint function `make`. It wasn't panicking when cap > len
1 parent 6d003ad commit 59acb38

16 files changed

+307
-28
lines changed

gnovm/pkg/gnolang/alloc.go

+7
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,20 @@ func (alloc *Allocator) NewString(s string) StringValue {
194194
}
195195

196196
func (alloc *Allocator) NewListArray(n int) *ArrayValue {
197+
if n < 0 {
198+
panic(&Exception{Value: typedString("len out of range")})
199+
}
197200
alloc.AllocateListArray(int64(n))
198201
return &ArrayValue{
199202
List: make([]TypedValue, n),
200203
}
201204
}
202205

203206
func (alloc *Allocator) NewDataArray(n int) *ArrayValue {
207+
if n < 0 {
208+
panic(&Exception{Value: typedString("len out of range")})
209+
}
210+
204211
alloc.AllocateDataArray(int64(n))
205212
return &ArrayValue{
206213
Data: make([]byte, n),

gnovm/pkg/gnolang/debugger_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func TestDebug(t *testing.T) {
158158
{in: "up xxx", out: `"xxx": invalid syntax`},
159159
{in: "b 37\nc\np b\n", out: "(3 int)"},
160160
{in: "b 27\nc\np b\n", out: `("!zero" string)`},
161-
{in: "b 22\nc\np t.A[3]\n", out: "Command failed: slice index out of bounds: 3 (len=3)"},
161+
{in: "b 22\nc\np t.A[3]\n", out: "Command failed: &{(\"slice index out of bounds: 3 (len=3)\" string) <nil> }"},
162162
{in: "b 43\nc\nc\nc\np i\ndetach\n", out: "(1 int)"},
163163
})
164164

gnovm/pkg/gnolang/machine.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,9 @@ func (m *Machine) RunFunc(fn Name) {
829829

830830
func (m *Machine) RunMain() {
831831
defer func() {
832-
if r := recover(); r != nil {
832+
r := recover()
833+
834+
if r != nil {
833835
switch r := r.(type) {
834836
case UnhandledPanicError:
835837
fmt.Printf("Machine.RunMain() panic: %s\nStacktrace: %s\n",
@@ -1280,6 +1282,20 @@ const (
12801282
// main run loop.
12811283

12821284
func (m *Machine) Run() {
1285+
defer func() {
1286+
r := recover()
1287+
1288+
if r != nil {
1289+
switch r := r.(type) {
1290+
case *Exception:
1291+
m.Panic(r.Value)
1292+
m.Run()
1293+
default:
1294+
panic(r)
1295+
}
1296+
}
1297+
}()
1298+
12831299
for {
12841300
if m.Debugger.enabled {
12851301
m.Debug()

gnovm/pkg/gnolang/op_assign.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ func (m *Machine) doOpQuoAssign() {
131131
}
132132
}
133133
// lv /= rv
134-
quoAssign(lv.TV, rv)
134+
err := quoAssign(lv.TV, rv)
135+
if err != nil {
136+
panic(err)
137+
}
138+
135139
if lv.Base != nil {
136140
m.Realm.DidUpdate(lv.Base.(Object), nil, nil)
137141
}
@@ -154,7 +158,11 @@ func (m *Machine) doOpRemAssign() {
154158
}
155159
}
156160
// lv %= rv
157-
remAssign(lv.TV, rv)
161+
err := remAssign(lv.TV, rv)
162+
if err != nil {
163+
panic(err)
164+
}
165+
158166
if lv.Base != nil {
159167
m.Realm.DidUpdate(lv.Base.(Object), nil, nil)
160168
}

gnovm/pkg/gnolang/op_binary.go

+104-4
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,10 @@ func (m *Machine) doOpQuo() {
252252
}
253253

254254
// lv / rv
255-
quoAssign(lv, rv)
255+
err := quoAssign(lv, rv)
256+
if err != nil {
257+
panic(err)
258+
}
256259
}
257260

258261
func (m *Machine) doOpRem() {
@@ -266,7 +269,10 @@ func (m *Machine) doOpRem() {
266269
}
267270

268271
// lv % rv
269-
remAssign(lv, rv)
272+
err := remAssign(lv, rv)
273+
if err != nil {
274+
panic(err)
275+
}
270276
}
271277

272278
func (m *Machine) doOpShl() {
@@ -845,45 +851,94 @@ func mulAssign(lv, rv *TypedValue) {
845851
}
846852

847853
// for doOpQuo and doOpQuoAssign.
848-
func quoAssign(lv, rv *TypedValue) {
854+
func quoAssign(lv, rv *TypedValue) *Exception {
855+
expt := &Exception{
856+
Value: typedString("division by zero"),
857+
}
858+
849859
// set the result in lv.
850860
// NOTE this block is replicated in op_assign.go
851861
switch baseOf(lv.T) {
852862
case IntType:
863+
if rv.GetInt() == 0 {
864+
return expt
865+
}
853866
lv.SetInt(lv.GetInt() / rv.GetInt())
854867
case Int8Type:
868+
if rv.GetInt8() == 0 {
869+
return expt
870+
}
855871
lv.SetInt8(lv.GetInt8() / rv.GetInt8())
856872
case Int16Type:
873+
if rv.GetInt16() == 0 {
874+
return expt
875+
}
857876
lv.SetInt16(lv.GetInt16() / rv.GetInt16())
858877
case Int32Type, UntypedRuneType:
878+
if rv.GetInt32() == 0 {
879+
return expt
880+
}
859881
lv.SetInt32(lv.GetInt32() / rv.GetInt32())
860882
case Int64Type:
883+
if rv.GetInt64() == 0 {
884+
return expt
885+
}
861886
lv.SetInt64(lv.GetInt64() / rv.GetInt64())
862887
case UintType:
888+
if rv.GetUint() == 0 {
889+
return expt
890+
}
863891
lv.SetUint(lv.GetUint() / rv.GetUint())
864892
case Uint8Type:
893+
if rv.GetUint8() == 0 {
894+
return expt
895+
}
865896
lv.SetUint8(lv.GetUint8() / rv.GetUint8())
866897
case DataByteType:
898+
if rv.GetUint8() == 0 {
899+
return expt
900+
}
867901
lv.SetDataByte(lv.GetDataByte() / rv.GetUint8())
868902
case Uint16Type:
903+
if rv.GetUint16() == 0 {
904+
return expt
905+
}
869906
lv.SetUint16(lv.GetUint16() / rv.GetUint16())
870907
case Uint32Type:
908+
if rv.GetUint32() == 0 {
909+
return expt
910+
}
871911
lv.SetUint32(lv.GetUint32() / rv.GetUint32())
872912
case Uint64Type:
913+
if rv.GetUint64() == 0 {
914+
return expt
915+
}
873916
lv.SetUint64(lv.GetUint64() / rv.GetUint64())
874917
case Float32Type:
875918
// NOTE: gno doesn't fuse *+.
919+
if rv.GetFloat32() == 0 {
920+
return expt
921+
}
876922
lv.SetFloat32(lv.GetFloat32() / rv.GetFloat32())
877923
// XXX FOR DETERMINISM, PANIC IF NAN.
878924
case Float64Type:
879925
// NOTE: gno doesn't fuse *+.
926+
if rv.GetFloat64() == 0 {
927+
return expt
928+
}
880929
lv.SetFloat64(lv.GetFloat64() / rv.GetFloat64())
881930
// XXX FOR DETERMINISM, PANIC IF NAN.
882931
case BigintType, UntypedBigintType:
932+
if rv.GetBigInt().Sign() == 0 {
933+
return expt
934+
}
883935
lb := lv.GetBigInt()
884936
lb = big.NewInt(0).Quo(lb, rv.GetBigInt())
885937
lv.V = BigintValue{V: lb}
886938
case BigdecType, UntypedBigdecType:
939+
if rv.GetBigDec().Cmp(apd.New(0, 0)) == 0 {
940+
return expt
941+
}
887942
lb := lv.GetBigDec()
888943
rb := rv.GetBigDec()
889944
quo := apd.New(0, 0)
@@ -898,36 +953,79 @@ func quoAssign(lv, rv *TypedValue) {
898953
lv.T,
899954
))
900955
}
956+
957+
return nil
901958
}
902959

903960
// for doOpRem and doOpRemAssign.
904-
func remAssign(lv, rv *TypedValue) {
961+
func remAssign(lv, rv *TypedValue) *Exception {
962+
expt := &Exception{
963+
Value: typedString("division by zero"),
964+
}
965+
905966
// set the result in lv.
906967
// NOTE this block is replicated in op_assign.go
907968
switch baseOf(lv.T) {
908969
case IntType:
970+
if rv.GetInt() == 0 {
971+
return expt
972+
}
909973
lv.SetInt(lv.GetInt() % rv.GetInt())
910974
case Int8Type:
975+
if rv.GetInt8() == 0 {
976+
return expt
977+
}
911978
lv.SetInt8(lv.GetInt8() % rv.GetInt8())
912979
case Int16Type:
980+
if rv.GetInt16() == 0 {
981+
return expt
982+
}
913983
lv.SetInt16(lv.GetInt16() % rv.GetInt16())
914984
case Int32Type, UntypedRuneType:
985+
if rv.GetInt32() == 0 {
986+
return expt
987+
}
915988
lv.SetInt32(lv.GetInt32() % rv.GetInt32())
916989
case Int64Type:
990+
if rv.GetInt64() == 0 {
991+
return expt
992+
}
917993
lv.SetInt64(lv.GetInt64() % rv.GetInt64())
918994
case UintType:
995+
if rv.GetUint() == 0 {
996+
return expt
997+
}
919998
lv.SetUint(lv.GetUint() % rv.GetUint())
920999
case Uint8Type:
1000+
if rv.GetUint8() == 0 {
1001+
return expt
1002+
}
9211003
lv.SetUint8(lv.GetUint8() % rv.GetUint8())
9221004
case DataByteType:
1005+
if rv.GetUint8() == 0 {
1006+
return expt
1007+
}
9231008
lv.SetDataByte(lv.GetDataByte() % rv.GetUint8())
9241009
case Uint16Type:
1010+
if rv.GetUint16() == 0 {
1011+
return expt
1012+
}
9251013
lv.SetUint16(lv.GetUint16() % rv.GetUint16())
9261014
case Uint32Type:
1015+
if rv.GetUint32() == 0 {
1016+
return expt
1017+
}
9271018
lv.SetUint32(lv.GetUint32() % rv.GetUint32())
9281019
case Uint64Type:
1020+
if rv.GetUint64() == 0 {
1021+
return expt
1022+
}
9291023
lv.SetUint64(lv.GetUint64() % rv.GetUint64())
9301024
case BigintType, UntypedBigintType:
1025+
if rv.GetBigInt().Sign() == 0 {
1026+
return expt
1027+
}
1028+
9311029
lb := lv.GetBigInt()
9321030
lb = big.NewInt(0).Rem(lb, rv.GetBigInt())
9331031
lv.V = BigintValue{V: lb}
@@ -937,6 +1035,8 @@ func remAssign(lv, rv *TypedValue) {
9371035
lv.T,
9381036
))
9391037
}
1038+
1039+
return nil
9401040
}
9411041

9421042
// for doOpBand and doOpBandAssign.

gnovm/pkg/gnolang/op_expressions.go

+4
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ func (m *Machine) doOpStar() {
145145
xv := m.PopValue()
146146
switch bt := baseOf(xv.T).(type) {
147147
case *PointerType:
148+
if xv.V == nil {
149+
panic(&Exception{Value: typedString("nil pointer dereference")})
150+
}
151+
148152
pv := xv.V.(PointerValue)
149153
if pv.TV.T == DataByteType {
150154
tv := TypedValue{T: bt.Elt}

gnovm/pkg/gnolang/uverse.go

+5
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,11 @@ func makeUverseNode() {
838838
li := lv.ConvertGetInt()
839839
cv := vargs.TV.GetPointerAtIndexInt(m.Store, 1).Deref()
840840
ci := cv.ConvertGetInt()
841+
842+
if ci < li {
843+
panic(&Exception{Value: typedString(`makeslice: cap out of range`)})
844+
}
845+
841846
if et.Kind() == Uint8Kind {
842847
arrayValue := m.Alloc.NewDataArray(ci)
843848
m.PushValue(TypedValue{

0 commit comments

Comments
 (0)