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

[Neo Core Bug]fix 3300 #3301

Merged
merged 15 commits into from
Jun 11, 2024
Merged

[Neo Core Bug]fix 3300 #3301

merged 15 commits into from
Jun 11, 2024

Conversation

Jim8y
Copy link
Contributor

@Jim8y Jim8y commented Jun 7, 2024

Description

This pr fixes the issue 3300

Fixes # #3300

Type of change

  • Optimization (the change is only an optimization)
  • Style (the change is only a code style for better maintenance or standard purpose)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Test Configuration:

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@Jim8y Jim8y added Waiting for Review Critical Issues (bugs) that need to be fixed ASAP Bug Used to tag confirmed bugs labels Jun 7, 2024
@Jim8y Jim8y requested a review from a team June 7, 2024 05:40
@dusmart
Copy link

dusmart commented Jun 7, 2024

You caught me so quickly. This GetNotifications is exactly what I'm exploiting with.

@Jim8y Jim8y added the Blocked This issue can't be worked at the moment label Jun 7, 2024
@dusmart
Copy link

dusmart commented Jun 7, 2024

Your PR is definitely a good one. It not only fixed my issue, but also fixed another issue.

Considering this attack vector (seems that I can't come up with a POC because of the immutable copy from Runtime.Notify).

  1. contract A send a notification NA
  2. contract B relies on that notification NA and reads that, then changed it to NA'
  3. contract C also relies on that notification, and it can only get the NA' because the State is not deep copied

private void DecreaseCounter(int count = 1)
{
references_count -= count;
if (references_count < 0) throw new InvalidOperationException("Reference counter is negative.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But how is it ever possible? Have you faced with the issue when refcount is negative?

Copy link
Contributor Author

@Jim8y Jim8y Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, its @dusmart who found the issue. With Array object deepcopied and removed from the stack, all of its references are removed as well, when we add that copied array to the stack, its internal subitems will not be added to the reference counter, but when we remove that copied item from the stack again, the counter will consider the array subitems.

BArray = AArray.DeepCopy

Remove BArray from stack // this will remove BArray and its subitem from the counter.

Aadd BArray back to stack // This will only add BArray itself back to the counter

Remove BArray from stack // This will remove BArray and its subitems from the counter, and create minus counter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AArray.DeepCopy doesn't increase the counter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AArray.DeepCopy doesn't increase the counter?

It should increase the counter:

referenceCounter.AddReference(item, this);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (references_count < 0) throw new InvalidOperationException("Reference counter is negative.");

I think we should not change this code. It's a bug in the handlers implementation, not in the reference counter logic. So we should fix the source problem, not its consequence.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. reference counter is supposed to be not negative, it should be the counter's property, should not rely on the callers.

@Jim8y let's discuss it here, we have a designated thread for it. If we add this change, then it may affect some old transactions. And I'd like to see @roman-khimov's opinion on this topic.

Copy link
Contributor Author

@Jim8y Jim8y Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? isn't you saying its supposed to be positive? then what problem can this change cause? It happened to be negative in the past?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest to move the reference counter into another pr and keep the fix simple

src/Neo/SmartContract/ApplicationEngine.Runtime.cs Outdated Show resolved Hide resolved
private void DecreaseCounter(int count = 1)
{
references_count -= count;
if (references_count < 0) throw new InvalidOperationException("Reference counter is negative.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AArray.DeepCopy doesn't increase the counter?

It should increase the counter:

referenceCounter.AddReference(item, this);

private void DecreaseCounter(int count = 1)
{
references_count -= count;
if (references_count < 0) throw new InvalidOperationException("Reference counter is negative.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (references_count < 0) throw new InvalidOperationException("Reference counter is negative.");

I think we should not change this code. It's a bug in the handlers implementation, not in the reference counter logic. So we should fix the source problem, not its consequence.

@@ -69,7 +69,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
ScriptHash.ToArray(),
EventName,
State
State.OnStack ? State : State.DeepCopy(true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that this change is the one that actually fixes the problem. But the problem is that it requires one more hardfork if we care about safety since it changes the behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just apply to the hardfork you created. will update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardfork has being applied.

Jim8y added 3 commits June 7, 2024 20:30
* 'fix-3300' of github.com:Jim8y/neo:
  Fixed up `main.yml` (neo-project#3292)
  [Neo Core Style] Rename parameters, update comments, fix code style (neo-project#3294)
@Jim8y
Copy link
Contributor Author

Jim8y commented Jun 7, 2024

#3301 (comment)

I can not agree, counter can not and should not be negative, that should not be ensured by outside callers, but should be an internal feature, we have showed you the case where the counter can be negative, how could you sure that others who write the code will not cause the same problem.

@Jim8y
Copy link
Contributor Author

Jim8y commented Jun 7, 2024

#3301 (comment)

We know it can increase the counter, but it decrease after the deepcopy command finishes. As the deepcopied value is not on the stack.

@Jim8y Jim8y removed the Blocked This issue can't be worked at the moment label Jun 7, 2024
shargon
shargon previously approved these changes Jun 7, 2024
Copy link
Member

@cschuchardt88 cschuchardt88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a test?

@dusmart
Copy link

dusmart commented Jun 8, 2024

@dusmartcan you help to perform a test

Seems that master branch is not suitable for me. When I build the NEO.CLI locally on master branch, it keeps fail because it tries to create files under my /bin/ path. Information are like this Access to the path '/bin/Neo.CLI' is denied..

I'm going to apply these changes manually upon v3.7.4

@dusmart
Copy link

dusmart commented Jun 8, 2024

tested OK with applying diff to v3.7.4

diff --git a/src/Neo/SmartContract/NotifyEventArgs.cs b/src/Neo/SmartContract/NotifyEventArgs.cs
index 93c124ea..d99b0426 100644
--- a/src/Neo/SmartContract/NotifyEventArgs.cs
+++ b/src/Neo/SmartContract/NotifyEventArgs.cs
@@ -69,7 +69,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter)
             {
                 ScriptHash.ToArray(),
                 EventName,
-                State
+                State.DeepCopy(true)
             };
         }
     }
neo> invoke 0x179ab5d297fd34ecd48643894242fc3527f42853 bingo [{"type":"Integer","value":2000}]
Error: You have to open the wallet first.
Invoking script with: 'AdAHEcAfDAViaW5nbwwUUyj0JzX8QkKJQ4bU7DT9l9K1mhdBYn1bUg=='
VM State: FAULT
Gas Consumed: 0.12349045
Result Stack: []
Error: MaxStackSize exceed: 152010

Jim8y added 2 commits June 8, 2024 18:46
* 'fix-3300' of github.com:Jim8y/neo:
  Fix error when vc_redist is missing (neo-project#3293)
@Jim8y Jim8y dismissed stale reviews from vncoelho and shargon via 2e742cc June 8, 2024 10:47
@NGDAdmin NGDAdmin merged commit b1d27f0 into neo-project:master Jun 11, 2024
6 of 7 checks passed
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Jun 11, 2024
This test ensures that NeoGo node doesn't have the DeepCopy problem
described in neo-project/neo#3300 and fixed in
neo-project/neo#3301. This problem leads to the
fact that Notifications items are not being properly refcounted by C#
node which leads to possibility to build an enormously large object on
stack. Go node doesn't have this problem.

The reason (at least, as I understand it) is in the fact that C# node
performs objects refcounting inside the DeepCopy even if the object
itself is not yet on stack. I.e. System.Runtime.Notify handler
immediately adds references to the notification argumetns inside
DeepCopy:
https://github.com/neo-project/neo/blob/b1d27f0189861167b8005a7e40e6d8500fb48cc4/src/Neo.VM/Types/Array.cs#L108
https://github.com/neo-project/neo/blob/b1d27f0189861167b8005a7e40e6d8500fb48cc4/src/Neo.VM/Types/Array.cs#L75

Whereas Go node just performs the honest DeepCopy without references counting:
https://github.com/nspcc-dev/neo-go/blob/b66cea5cccbcc8446312b012e29aaf2d1837430a/pkg/vm/stackitem/item.go#L1223

Going further, C# node clears refs for notification arguments (for array
and underlying array items). System.Runtime.GetNotifications pushes the
notificaiton args array back on stack and increments counter only for
the external array, not for its arguments. Which results in negative
refcounter once notificaiton is removed from the stack. The fix itself
(https://github.com/Jim8y/neo/blob/f471c0542ddd8f527478fbdcda76a3ab9194b958/src/Neo/SmartContract/NotifyEventArgs.cs#L84)
doesn't need to be ported to NeoGo because Go node adds object to the
refcounter only at the moment when it's being pushed to stack by
System.Runtime.GetNotifications handler. This object is treated as new
object since it was deepcopied earlier by System.Runtime.Notify handler:
https://github.com/nspcc-dev/neo-go/blob/b66cea5cccbcc8446312b012e29aaf2d1837430a/pkg/vm/stack.go#L178.

Thus, no functoinal changes from the NeoGo side. And we won't
intentionally break our node to follow C# pre-Domovoi invalid behaviour.

Close #3484, close #3482.

Signed-off-by: Anna Shaleva <[email protected]>
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Jun 11, 2024
Add a note about System.Runtime.GetNotifications refcounting to Domovoi
hardfork. Ref. neo-project/neo#3301 and
#3485.

Although NeoGo doesn't have anything to be updated, there's a
behaviour difference between C# and Go nodes before Domovoi hardfork, it
deserves a comment.

Signed-off-by: Anna Shaleva <[email protected]>
NGDAdmin added a commit that referenced this pull request Jun 12, 2024
* [Neo Core Bug]fix 3300 (#3301)

* fix 3300

* update format

* add state subitems to ref counter, with suggestion from DuSmart

* apply hardfork

* format

* my mistake

* fix hardfork

* remove negative check

* add unit test

* apply anna's suggestion

---------

Co-authored-by: Shargon <[email protected]>
Co-authored-by: NGD Admin <[email protected]>

* SmartContract: use executing contract state to check permissions (#3290)

It's not correct to use an updated contract state got from native
Management to check for the allowed method call. We need to use
manifest from the currently executing context for that. It may be
critical for cases when executing contract is being updated firstly,
and after that it calls another contract. So we need an old (executing)
contract manifest for this check.

This change is moved under D hardfork to avoid state diff issues on
nodes update. Although it should be noted that it's hard to meet the
trigger criteria.

A port of nspcc-dev/neo-go#3473. This bug was
discovered during the similar problem described in
nspcc-dev/neo-go#3471 and fixed in
nspcc-dev/neo-go#3472. I've checked all other
similar usages and the rest of them use proper contract state (executing
one, not the Management's one).

Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Shargon <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Vitor Nazário Coelho <[email protected]>

* v3.7.5

* Neo.CLI: enable hardforks for NeoFS mainnet (#3240)

Otherwise this configuration file is broken. Port changes from
nspcc-dev/neo-go#3446.

Signed-off-by: Anna Shaleva <[email protected]>

* fix workflow & FS config

* remove hardfork for fs testnet

---------

Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Shargon <[email protected]>
Co-authored-by: NGD Admin <[email protected]>
Co-authored-by: Anna Shaleva <[email protected]>
Co-authored-by: Vitor Nazário Coelho <[email protected]>
@Jim8y Jim8y deleted the fix-3300 branch July 19, 2024 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Used to tag confirmed bugs Critical Issues (bugs) that need to be fixed ASAP Hardfork Ready to Merge Waiting for Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

stack size can exceed MaxStackSize
8 participants