-
Notifications
You must be signed in to change notification settings - Fork 375
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
RFC Proposal for a new stack/frames management #683
Comments
After a lot of works on all What's the problem ?The package Which could lead to a lot of security issue (phishing for example), see this article for example So how do we manage the treasury of a realm ? Solutions❌ The first idea was I believe that by default that the caller should be the realm if the realm is a caller. ✅ IMHO #667 ( there is an ongoing question about this feature, i'd love your inputs Some proposalsBy working on
Thoughts
|
I share most of @albttx's opinion on this. I don't think giving access to all the callers and frames to the smart contract developer is really needed. By restricting to the last caller via About |
Yes, I get your point. Imagine this scenario:
With By returning frames with both the caller and the current In my opinion, It should be noted that the I propose implementing the Edit: We have more flexibility thanks to a rich stack and something similar to |
Ok, i see what's your point, Let's me clarify, with
We don't need SetUID for that, this is what And you can easily add in your You could use something like that to exec as your vault func Exec(fn func()) {
if whitelistCallers.Contains(std.GetCaller) {
fn()
}
} Also, in a phishing concern, a malicious realm could make advantage of a SetUID system and exploit that. |
Yes, I listed this example before your comment. Clarification: Our discussion mainly focuses on returning the full call stack rather than just the last caller, while While both approaches provide the same level of security for the Implementing the Blockchains are not made for DeFi, especially Gno. Let's ask the Reminder: My proposal is to use std.Callers() std.Frames and gno.land/p/demo/rules for helpers, instead of std.Get*Caller.
Edit: Another proposal: Updating |
- Not sure Tx.Origin Fishing Attack applies to Gnotx.Origin attack works because the Solidity contract's fallback function can be invoked without calling any method's name, like an anonymous function. From the end-user perspective, there is no difference between sending tokens to a person’s EOA or a phishing contract because they all send tokens to an address. Users thought they transferred tokens to a person, but in reality, they sent them to a phishing contract, which can use tx.Origin to construct a contract call in the fallback function to transfer funds to anyone and the message appears from Origin Caller to valuable contract. Checking immediate msg.Sender in Solidity has their own trust and security trade-off. In GNO, there is no contract fallback function (anonymous function) for end users to call upon. All function calls to the realm package require exported function name. Checking OrigCaller prevents malicious contracts in personating a request from end users. |
Although this particular attack may not apply, we should be prepared for other potential attacks that could arise. I believe that we should not use this as a reason to remove an important feature for end developers. Even if we implement my suggestion with a richer stack and SetUID, we can still create an exceptionally secure flow for grc20. I'll create a new issue to suggest security improvements. |
IMHO, It seems that
|
This makes sense. For ERC-20, approve and allowance are mechanisms designed to solve specific problems. From a more general perspective, these mechanisms should ideally be supported by more fundamental and generic methods at the lower level. |
Fix gnolang#481 `AssertOriginCall` and `IsOriginCall` functions can be used inside contracts to determine if the call comes directly from a tx (which is an *origin call*) or from an other contract (not an *origin call*). To do this those functions count the number of frames. In production environment, if there's exactly 2 frames, it is considered as an *origin call*. In test environment, the number of frames was 3, but it was working only for `_filetest`, and not for `_test`, where the setup is different and so the number of frames (7 instead of 3). To support `_test`, the change brings a way to identify if a test is a `_test` or `_filetest`, by checking the function name of the first frame. There's currently a pending discussion (gnolang#683) about how we should deal with frames, that's why I tried to keep this change minimal, but at least it fixes something and we can move forward. Ideally, `*OriginCall` functions should rely on something stronger that the number of frames, because for instance they could consider that a call is not an *origin call*, just because `std.IsOriginCall` is called by a sub-function of the same contract. But that's something we'll probably tackle with gnolang#683.
Feedback from the last private discussion:
|
I'm glad we're taking this slow. Here's a related suggestion: https://github.com/gnolang/gno/pull/667/files#r1169454363 This is to implement Get[Realm]Caller(). There is a question of whether we want to return rich frame information. It's certainly useful for debugging panics in a program. But also, this could be implemented separately later with functions like "GetCallerFrames" or "GetCallerFrameAt". So I suggest that we keep the functionality of GetOrigCaller and GetCallerAt() the same for now, and also to add GetRealmCaller(), where all *Caller[At] functions return an address for now, and that we spec out what a frame looks like in a separate PR and figure out how best to make it accessible to the programmer after. |
We discussed today about this topic (#667 (review)). We should:
type Frame struct {
PkgPath string
Method string // maybe later
} Consider introducing a new API for realm retrieval, excluding packages:
|
Context
The stack/frames is an integral part of the virtual machine (VM) and the language, and we rely heavily on it. Additionally, stack/frames provides a context for smart-contract developers, enabling them to access useful information such as the original caller or to determine if a contract is being called through another one.
The current implementation of the helpers
GetOrigCaller
andGetCallerAt
(found at https://github.com/gnolang/gno/blob/41af8f6daa54e949c77e04b375b7f3dd6ae0df71/stdlibs/stdlibs.go) is limited for advanced rules. It also relies on fixed positions in the stack, and can lead to inconsistencies when the runtime is configured differently, such as when unit testing with_test.gno
or_filetest.gno
.Continuous efforts are being made to enhance various components and expand functionalities, as evidenced by issues #335, #393, #402, #644, #667, #495.
Proposal
I propose a comprehensive solution that could address all the topics discussed, or serve as a foundation to build upon.
The stack entries in this context are immutable, but custom rules can be added to the stack. This is similar to how UNIX processes work with their Process ID (PID), forks, and UID management. Additionally, LXC provides an extra virtual layer for isolation and interoperability.
To enhance #644, consider renaming
ExecAsPkg
toSetUID
or enabling the called contract to recognize the calling contract as its own caller by adding a new stack entry for the passed closure.To make the necessary changes, replace
std.GetOrigCaller
andstd.GetCallerAt
withstd.Callers() std.Frames
. This function returns a slice of callers, each represented by a simple struct that containspid
,uid
, andStackType
. Thepid
-like entries in the slice function like a process ID, while theuid
changes only with helpers likeSetUID
. TheStackType
field facilitates the identification of transactions, contracts, VM events, and custom context entries.Here is an example that demonstrates the use of std.Callers() std.Stack and its resulting structure:
To include a helper (
gno.land/p/demo/rules
) package for Gno, with clearly named helpers likerules.GetOrigCaller(stack)
,rules.IsDirectCall(stack)
,rules.IsIndirectCall(stack)
,rules.IsCalledThroughPkg(stack, "gno.land/r/foobar")
. Additionally, there is an associated issue that can be found at #301.WDYT?
The text was updated successfully, but these errors were encountered: