diff --git a/examples/gno.land/p/demo/tests/tests.gno b/examples/gno.land/p/demo/tests/tests.gno index c4b8f93d52d..632bc2d888f 100644 --- a/examples/gno.land/p/demo/tests/tests.gno +++ b/examples/gno.land/p/demo/tests/tests.gno @@ -40,3 +40,17 @@ func ModifyTestRealmObject2b() { func ModifyTestRealmObject2c() { SomeValue3.Field = "modified" } + +func GetCaller() std.Address { + return std.GetCaller() +} + +func PrintCallers() { + println("p/TEST : ORIGCALL:", std.GetOrigCaller()) + println("p/TEST : PKG ADDR:", std.GetOrigPkgAddr()) + println("p/TEST : CALLER :", std.GetCaller()) +} + +func Exec(fn func()) { + fn() +} diff --git a/examples/gno.land/r/demo/tests/fake20/fake20.gno b/examples/gno.land/r/demo/tests/fake20/fake20.gno new file mode 100644 index 00000000000..196e648dcb0 --- /dev/null +++ b/examples/gno.land/r/demo/tests/fake20/fake20.gno @@ -0,0 +1,111 @@ +package fake20 + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" +) + +var ( + foo *grc20.AdminToken + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // TODO: helper to change admin +) + +func init() { + foo = grc20.NewAdminToken("Fake", "FAKE", 4) + foo.Mint(admin, 1000000*10000) // @administrator (1M) + foo.Mint("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", 10000*10000) // @manfred (10k) +} + +// method proxies as public functions. +// + +// getters. + +func TotalSupply() uint64 { + return foo.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := foo.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := foo.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +// setters. + +func Transfer(to users.AddressOrName, amount uint64) { + caller := std.GetCaller() + foo.Transfer(caller, to.Resolve(), amount) +} + +func Approve(spender users.AddressOrName, amount uint64) { + caller := std.GetCaller() + foo.Approve(caller, spender.Resolve(), amount) +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + caller := std.GetCaller() + foo.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) +} + +// faucet. + +func Faucet() { + // FIXME: add limits? + // FIXME: add payment in gnot? + caller := std.GetCaller() + foo.Mint(caller, 1000*10000) // 1k +} + +// administration. + +func Mint(address users.AddressOrName, amount uint64) { + caller := std.GetCaller() + assertIsAdmin(caller) + foo.Mint(address.Resolve(), amount) +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.GetCaller() + assertIsAdmin(caller) + foo.Burn(address.Resolve(), amount) +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return foo.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := foo.BalanceOf(owner.Resolve()) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} diff --git a/examples/gno.land/r/demo/tests/phishing/phishing.gno b/examples/gno.land/r/demo/tests/phishing/phishing.gno new file mode 100644 index 00000000000..d3970459b80 --- /dev/null +++ b/examples/gno.land/r/demo/tests/phishing/phishing.gno @@ -0,0 +1,4 @@ +package phising + +func GetMillionaire() { +} diff --git a/examples/gno.land/r/demo/tests/subtests/subtests.gno b/examples/gno.land/r/demo/tests/subtests/subtests.gno new file mode 100644 index 00000000000..c85549a7992 --- /dev/null +++ b/examples/gno.land/r/demo/tests/subtests/subtests.gno @@ -0,0 +1,17 @@ +package subtests + +import ( + "std" +) + +func CurrentRealmPath() string { + return std.CurrentRealmPath() +} + +func GetCaller() std.Address { + return std.GetCaller() +} + +func Exec(fn func()) { + fn() +} diff --git a/examples/gno.land/r/demo/tests/tests.gno b/examples/gno.land/r/demo/tests/tests.gno index dde1e7cad75..790de34e1d5 100644 --- a/examples/gno.land/r/demo/tests/tests.gno +++ b/examples/gno.land/r/demo/tests/tests.gno @@ -1,6 +1,10 @@ package tests -import "std" +import ( + "std" + + "gno.land/r/demo/tests/subtests" +) func CurrentRealmPath() string { return std.CurrentRealmPath() @@ -51,3 +55,27 @@ func ModTestNodes() { func PrintTestNodes() { println(gTestNode2.Child.Name) } + +func GetCaller() std.Address { + return std.GetCaller() +} + +func GetSubtestsCaller() std.Address { + return subtests.GetCaller() +} + +func ExecFromTest() { + Exec(func() { + println("ExecFromTest", std.GetCaller()) + }) +} + +func Exec(fn func()) { + fn() +} + +func PrintCallers() { + println("p/TEST : ORIGCALL:", std.GetOrigCaller()) + println("p/TEST : PKG ADDR:", std.GetOrigPkgAddr()) + println("p/TEST : CALLER :", std.GetCaller()) +} diff --git a/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm.gno b/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm.gno new file mode 100644 index 00000000000..957f9f293c8 --- /dev/null +++ b/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm.gno @@ -0,0 +1,44 @@ +package unsaferealm + +import ( + "std" + + "gno.land/p/demo/grc/grc20" +) + +var foo *grc20.AdminToken + +func init() { + foo = grc20.NewAdminToken("Fake", "FAKE", 4) + + // std.TestDerivePkgAddr("gno.land/r/demo/tests/unsaferealm") + foo.Mint("g1lpnflsxpr84dsqkznw85yd5wdzenkj89vsptmf", 1000000*10000) + // foo.Mint(std.GetOrigPkgAddr(), 1000000*10000) +} + +/* +** Some grc20 functions + */ + +func BalanceOf(owner std.Address) uint64 { + balance, err := foo.BalanceOf(owner) + if err != nil { + panic(err) + } + + return balance +} + +func Transfer(to std.Address, amount uint64) { + caller := std.GetCaller() + println("transfering", amount, "from:", caller, "to:", to) + foo.Transfer(caller, to, amount) +} + +/* +** Realm unsafe functions + */ + +func Do(fn func()) { + fn() +} diff --git a/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm_test.gno b/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm_test.gno new file mode 100644 index 00000000000..988dd16c238 --- /dev/null +++ b/examples/gno.land/r/demo/tests/unsaferealm/unsaferealm_test.gno @@ -0,0 +1,9 @@ +package unsaferealm + +import ( + "testing" +) + +func TestUnsafeRealm(t *testing.T) { + println("Hello") +} diff --git a/pkgs/gnolang/preprocess.go b/pkgs/gnolang/preprocess.go index 4c615188552..d0d8bf163a1 100644 --- a/pkgs/gnolang/preprocess.go +++ b/pkgs/gnolang/preprocess.go @@ -18,6 +18,7 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { SetNodeLocations(pn.PkgPath, string(fn.Name), fn) fn.InitStaticBlock(fn, pn) } + // NOTE: much of what follows is duplicated for a single *FileNode // in the main Preprocess translation function. Keep synced. @@ -41,6 +42,7 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { } } } + // Predefine all type decls decls. for _, fn := range fset.Files { for i := 0; i < len(fn.Decls); i++ { diff --git a/stdlibs/stdlibs.go b/stdlibs/stdlibs.go index b365e43a14e..f84a2a9d7a8 100644 --- a/stdlibs/stdlibs.go +++ b/stdlibs/stdlibs.go @@ -1,6 +1,7 @@ package stdlibs import ( + "fmt" "math" "reflect" "strconv" @@ -257,6 +258,36 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { m.PushValue(res0) }, ) + pn.DefineNative("GetCaller", + gno.Flds( // params + ), + gno.Flds( // results + "", "Address", + ), + func(m *gno.Machine) { + ctx := m.Context.(ExecContext) + + lastCaller := ctx.OrigCaller + + for i := m.NumFrames() - 1; i > 0; i-- { + frameA := m.Frames[i] + frameB := m.Frames[i-1] + if frameA.LastPackage.GetPkgAddr().String() != frameB.LastPackage.GetPkgAddr().String() { + lastCaller = frameB.LastPackage.GetPkgAddr().Bech32() + break + } + } + + res0 := gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(lastCaller), + ) + addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) + res0.T = addrT + m.PushValue(res0) + }, + ) pn.DefineNative("GetOrigPkgAddr", gno.Flds( // params ), @@ -264,7 +295,9 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { "", "Address", ), func(m *gno.Machine) { + fmt.Printf("GetOrigPkgAddr: %#v\n", m.Package) ctx := m.Context.(ExecContext) + res0 := gno.Go2GnoValue( m.Alloc, m.Store, diff --git a/tests/files/zrealm_crossrealm11.gno b/tests/files/zrealm_crossrealm11.gno new file mode 100644 index 00000000000..293bcda58ef --- /dev/null +++ b/tests/files/zrealm_crossrealm11.gno @@ -0,0 +1,46 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +import ( + "std" + + ptests "gno.land/p/demo/tests" + rtests "gno.land/r/demo/tests" +) + +func lol() { + println("HACK1", std.GetCaller()) + println("HACK2", rtests.GetCaller()) +} + +func main() { + println(`[DEBUG] + user1.gno: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm + crossrealm_test: g1vla5mffzum6060t99u4xhm8mnhgxr0sz4k574p + gno.land/r/demo/tests: g1gz4ycmx0s6ln2wdrsh4e00l9fsel2wskqa3snq + gno.land/p/demo/tests: g1lc7c8nv62nqyyhhxe88tpxx786gwq68prx3f6e + `) + + println("user1.gno -> gno.land/r/crossrealm_test: caller:", std.GetCaller()) + println("user1.gno -> gno.land/r/crossrealm_test -> gno.land/p/demo/tests: caller:", ptests.GetCaller()) + println("user1.gno -> gno.land/r/crossrealm_test -> gno.land/r/demo/tests: caller:", rtests.GetCaller()) // crossrealm -> gno.land/r/demo/tests + + rtests.Exec(func() { + println("r/EXEC1", std.GetCaller()) // ?? + println("r/EXEC2", ptests.GetCaller()) // ?? + }) + rtests.Exec(lol) + + ptests.Exec(func() { + println("p/EXEC1", std.GetCaller()) // ?? + println("p/EXEC2", ptests.GetCaller()) // ?? + }) + ptests.Exec(lol) + + println("3.c subtestsCaller", rtests.GetSubtestsCaller()) // gno.land/r/demo/tests -> gno.land/r/demo/tests/subtests + rtests.ExecFromTest() + println("4. main :", std.GetCaller()) +} + +// Output: +// struct{("modified" string)} diff --git a/tests/files/zrealm_crossrealm_exploit1_stdlibs.gno b/tests/files/zrealm_crossrealm_exploit1_stdlibs.gno new file mode 100644 index 00000000000..684043e7460 --- /dev/null +++ b/tests/files/zrealm_crossrealm_exploit1_stdlibs.gno @@ -0,0 +1,25 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +import ( + "std" + + "gno.land/r/demo/tests" + "gno.land/r/demo/tests/unsaferealm" +) + +func main() { + addr := std.GetCaller() + println("balance:", unsaferealm.BalanceOf(addr)) + + // Test to exploit from the Do function steal money from unsaferealm treasury + unsaferealm.Do(func() { + println("transfering", std.GetCaller()) + unsaferealm.Transfer(addr, 100000) + }) + + println("balance:", unsaferealm.BalanceOf(addr)) +} + +// Output: +// N/A