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 VM Bug] Fix negative counter issue #3327

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Neo.VM/ReferenceCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ internal int CheckZeroReferred()
tracked_items.Remove(item);
if (item is CompoundType compound)
{
references_count -= compound.SubItemsCount;
foreach (StackItem subitem in compound.SubItems)
{
if (component.Contains(subitem)) continue;
if (!NeedTrack(subitem)) continue;
subitem.ObjectReferences!.Remove(compound);
references_count--;
Copy link
Contributor

Choose a reason for hiding this comment

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

this ensures that it can be properly removed, but i think there is also problem of adding reference.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you have an example?

Copy link
Member

@cschuchardt88 cschuchardt88 Jun 16, 2024

Choose a reason for hiding this comment

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

I believe if it goes negative it means the item was never met from previous one StackItem. You can use the CachedComponents to see.

Copy link
Member

Choose a reason for hiding this comment

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

I'd expect references removal to be symmetric wrt references addition. And currently it's asymmetric because there's if (!NeedTrack(subitem)) continue; a few lines above; and during references addition we always increment refcounter irrespective of NeedTrack:

references_count++;
if (!NeedTrack(item)) return;

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, maybe we need to move the line before that if

Copy link
Member Author

Choose a reason for hiding this comment

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

It fails if is symmetric, we get a negative counter

Copy link
Member

Choose a reason for hiding this comment

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

The thing that I'm worried about is that we should find the reason why symmetric approach doesn't work. Right now I don't completely understand it.

shargon marked this conversation as resolved.
Show resolved Hide resolved
}
}
item.Cleanup();
Expand Down
37 changes: 37 additions & 0 deletions tests/Neo.VM.Tests/UT_ReferenceCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.VM;
using Neo.VM.Types;
using System;
using Array = Neo.VM.Types.Array;

namespace Neo.Test
{
Expand Down Expand Up @@ -240,5 +242,40 @@ public void TestArrayNoPush()
Assert.AreEqual(VMState.HALT, engine.Execute());
Assert.AreEqual(array.Count, engine.ReferenceCounter.Count);
}

[TestMethod]
public void TestNegativeReferenceCounter()
{
var referenceCounter = new ReferenceCounter();
var array = new Array(referenceCounter) { OnStack = true };
referenceCounter.AddStackReference(array);
for (var i = 0; i < 100; i++)
{
var item = new Integer(i) { OnStack = true };
array.Add(item);
}

Assert.AreEqual(101, referenceCounter.Count);
referenceCounter.CheckZeroReferred();
Assert.AreEqual(101, referenceCounter.Count);
var copyArray = array.DeepCopy(true);
Assert.AreEqual(201, referenceCounter.Count);
referenceCounter.CheckZeroReferred(); // Deepcopied value will be removed from the reference counter.
Assert.AreEqual(201, referenceCounter.Count);
// If we add the deepcopied value again, the reference counter will increase by only 1.
referenceCounter.AddStackReference(copyArray);
Assert.AreEqual(202, referenceCounter.Count);
referenceCounter.RemoveStackReference(copyArray);
// Removing the deepcopied value will decrease the reference counter by 101.
referenceCounter.CheckZeroReferred();
Assert.AreEqual(201, referenceCounter.Count);

// Bellow will trigger the negative reference counter exception
referenceCounter.AddStackReference(copyArray);
Assert.AreEqual(202, referenceCounter.Count);
referenceCounter.RemoveStackReference(copyArray);
referenceCounter.CheckZeroReferred();
Assert.AreEqual(201, referenceCounter.Count);
}
}
}