From 7ef606ce42b175d74b16ba3dd631fdb8926ab9d7 Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:40:17 +0100 Subject: [PATCH] fix(gnovm): prevent assignment to non-assignable expressions (#2896) closes: #2889
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- gnovm/pkg/gnolang/preprocess.go | 3 +++ gnovm/pkg/gnolang/type_check.go | 35 +++++++++++++++++++++++++++++++++ gnovm/tests/files/assign29.gno | 8 ++++++++ gnovm/tests/files/assign30.gno | 8 ++++++++ gnovm/tests/files/assign31.gno | 9 +++++++++ gnovm/tests/files/var31.gno | 8 ++++++++ gnovm/tests/files/var32.gno | 8 ++++++++ gnovm/tests/files/var33.gno | 9 +++++++++ 8 files changed, 88 insertions(+) create mode 100644 gnovm/tests/files/assign29.gno create mode 100644 gnovm/tests/files/assign30.gno create mode 100644 gnovm/tests/files/assign31.gno create mode 100644 gnovm/tests/files/var31.gno create mode 100644 gnovm/tests/files/var32.gno create mode 100644 gnovm/tests/files/var33.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index a7a1e15bbf3..78e4488b2a0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1961,6 +1961,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *AssignStmt: n.AssertCompatible(store, last) + // NOTE: keep DEFINE and ASSIGN in sync. if n.Op == DEFINE { // Rhs consts become default *ConstExprs. @@ -2291,6 +2292,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *ValueDecl: + assertValidAssignRhs(store, last, n) + // evaluate value if const expr. if n.Const { // NOTE: may or may not be a *ConstExpr, diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index f86c44e7921..c62d67375ee 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -837,6 +837,7 @@ func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) { func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { if x.Op == ASSIGN || x.Op == DEFINE { + assertValidAssignRhs(store, last, x) if len(x.Lhs) > len(x.Rhs) { if len(x.Rhs) != 1 { panic(fmt.Sprintf("assignment mismatch: %d variables but %d values", len(x.Lhs), len(x.Rhs))) @@ -997,6 +998,40 @@ func assertValidAssignLhs(store Store, last BlockNode, lx Expr) { } } +func assertValidAssignRhs(store Store, last BlockNode, n Node) { + var exps []Expr + switch x := n.(type) { + case *ValueDecl: + exps = x.Values + case *AssignStmt: + exps = x.Rhs + default: + panic(fmt.Sprintf("unexpected node type %T", n)) + } + + for _, exp := range exps { + tt := evalStaticTypeOfRaw(store, last, exp) + if tt == nil { + switch x := n.(type) { + case *ValueDecl: + if x.Type != nil { + continue + } + panic("use of untyped nil in variable declaration") + case *AssignStmt: + if x.Op != DEFINE { + continue + } + panic("use of untyped nil in assignment") + } + } + if _, ok := tt.(*TypeType); ok { + tt = evalStaticType(store, last, exp) + panic(fmt.Sprintf("%s (type) is not an expression", tt.String())) + } + } +} + func kindString(xt Type) string { if xt != nil { return xt.Kind().String() diff --git a/gnovm/tests/files/assign29.gno b/gnovm/tests/files/assign29.gno new file mode 100644 index 00000000000..ca605f5ecbe --- /dev/null +++ b/gnovm/tests/files/assign29.gno @@ -0,0 +1,8 @@ +package main + +func main() { + t := struct{} +} + +// Error: +// main/files/assign29.gno:4:2: struct{} (type) is not an expression diff --git a/gnovm/tests/files/assign30.gno b/gnovm/tests/files/assign30.gno new file mode 100644 index 00000000000..ad72f880f27 --- /dev/null +++ b/gnovm/tests/files/assign30.gno @@ -0,0 +1,8 @@ +package main + +func main() { + t := *struct{} +} + +// Error: +// main/files/assign30.gno:4:2: *struct{} (type) is not an expression diff --git a/gnovm/tests/files/assign31.gno b/gnovm/tests/files/assign31.gno new file mode 100644 index 00000000000..8c96c04501e --- /dev/null +++ b/gnovm/tests/files/assign31.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := nil + println(a) +} + +// Error: +// main/files/assign31.gno:4:2: use of untyped nil in assignment diff --git a/gnovm/tests/files/var31.gno b/gnovm/tests/files/var31.gno new file mode 100644 index 00000000000..813e6ff9e22 --- /dev/null +++ b/gnovm/tests/files/var31.gno @@ -0,0 +1,8 @@ +package main + +func main() { + var t = struct{} +} + +// Error: +// main/files/var31.gno:4:6: struct{} (type) is not an expression diff --git a/gnovm/tests/files/var32.gno b/gnovm/tests/files/var32.gno new file mode 100644 index 00000000000..827c3951f94 --- /dev/null +++ b/gnovm/tests/files/var32.gno @@ -0,0 +1,8 @@ +package main + +func main() { + var t = nil +} + +// Error: +// main/files/var32.gno:4:6: use of untyped nil in variable declaration diff --git a/gnovm/tests/files/var33.gno b/gnovm/tests/files/var33.gno new file mode 100644 index 00000000000..ce883dce47c --- /dev/null +++ b/gnovm/tests/files/var33.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var t *int = nil + println("pass") +} + +// Output: +// pass