Skip to content

Commit a95e71b

Browse files
authored
Merge branch 'master-n3' into n3-fix-4316
2 parents 6311aff + f7bc967 commit a95e71b

2 files changed

Lines changed: 99 additions & 4 deletions

File tree

src/Neo/SmartContract/ApplicationEngine.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ public static JumpTable ComposeNotGorgonJumpTable()
288288
table[OpCode.SETITEM] = SetItem_Before543;
289289
table[OpCode.REMOVE] = Remove_Before543;
290290

291+
// Before https://github.com/neo-project/neo-vm/pull/567.
292+
table[OpCode.SHR] = VulnerableSHR;
293+
table[OpCode.SHL] = VulnerableSHL;
294+
291295
return table;
292296
}
293297

@@ -711,6 +715,42 @@ private static void VulnerableSubStr(ExecutionEngine engine, Instruction instruc
711715
engine.Push(result);
712716
}
713717

718+
/// <summary>
719+
/// Computes the left shift of an integer. Vulnerable implementation of
720+
/// <see cref="OpCode.SHL"/> since it doesn't pop operand from stack in
721+
/// case of zero shift.
722+
/// </summary>
723+
/// <param name="engine">The execution engine.</param>
724+
/// <param name="instruction">The instruction being executed.</param>
725+
/// <remarks>Pop 2, Push 1</remarks>
726+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
727+
private static void VulnerableSHL(ExecutionEngine engine, Instruction instruction)
728+
{
729+
var shift = (int)engine.Pop().GetInteger();
730+
engine.Limits.AssertShift(shift);
731+
if (shift == 0) return;
732+
var x = engine.Pop().GetInteger();
733+
engine.Push(x << shift);
734+
}
735+
736+
/// <summary>
737+
/// Computes the right shift of an integer. Vulnerable implementation of
738+
/// <see cref="OpCode.SHR"/> since it doesn't pop operand from stack in
739+
/// case of zero shift.
740+
/// </summary>
741+
/// <param name="engine">The execution engine.</param>
742+
/// <param name="instruction">The instruction being executed.</param>
743+
/// <remarks>Pop 2, Push 1</remarks>
744+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
745+
private static void VulnerableSHR(ExecutionEngine engine, Instruction instruction)
746+
{
747+
var shift = (int)engine.Pop().GetInteger();
748+
engine.Limits.AssertShift(shift);
749+
if (shift == 0) return;
750+
var x = engine.Pop().GetInteger();
751+
engine.Push(x >> shift);
752+
}
753+
714754
public override void LoadContext(ExecutionContext context)
715755
{
716756
// Set default execution context state

tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.JumpTable.cs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,54 @@ namespace Neo.UnitTests.SmartContract
2020
{
2121
public partial class UT_ApplicationEngine
2222
{
23+
[TestMethod]
24+
public void TestSHR_SHL_WithDifferentHF()
25+
{
26+
foreach (var op in new OpCode[] { OpCode.SHL, OpCode.SHR })
27+
{
28+
using var sb = new ScriptBuilder();
29+
30+
sb.Emit(OpCode.NEWARRAY0); // intentionally wrong argument for SHL or SHR.
31+
sb.EmitPush(0); // zero shift.
32+
sb.Emit(op);
33+
34+
var script = sb.ToArray();
35+
36+
const uint EchidnaEnable = 10u;
37+
const uint GorgonEnable = 20u;
38+
// Hardfork heights:
39+
// Echidna at 10, Gorgon at 20
40+
// - index=5 => pre-Echidna (NotEchidnaJumpTable)
41+
// - index=15 => Echidna enabled, Gorgon NOT enabled (NotGorgonJumpTable)
42+
// - index=30 => Gorgon enabled (DefaultJumpTable)
43+
var settings = ProtocolSettings.Default with
44+
{
45+
Hardforks = ProtocolSettings.Default.Hardforks
46+
.SetItem(Hardfork.HF_Echidna, EchidnaEnable)
47+
.SetItem(Hardfork.HF_Gorgon, GorgonEnable)
48+
};
49+
50+
Assert.IsFalse(settings.IsHardforkEnabled(Hardfork.HF_Echidna, 5u));
51+
Assert.IsTrue(settings.IsHardforkEnabled(Hardfork.HF_Echidna, 15u));
52+
Assert.IsTrue(settings.IsHardforkEnabled(Hardfork.HF_Echidna, 30u));
53+
Assert.IsFalse(settings.IsHardforkEnabled(Hardfork.HF_Gorgon, 15u));
54+
Assert.IsTrue(settings.IsHardforkEnabled(Hardfork.HF_Gorgon, 30u));
55+
56+
// Case A: pre-Echidna => no exception.
57+
var engine = Execute(script, settings, index: 5u);
58+
Assert.AreEqual(VMState.HALT, engine.State);
59+
Assert.AreEqual(1, engine.ResultStack.Count);
60+
61+
// Case B: Echidna enabled but pre-Gorgon => no exception.
62+
engine = Execute(script, settings, index: 15u);
63+
Assert.AreEqual(VMState.HALT, engine.State);
64+
Assert.AreEqual(1, engine.ResultStack.Count);
65+
66+
// Case C: Gorgon enabled => InvalidCastException on attempt to convert Array to Integer.
67+
ExecuteAndAssertFault<InvalidCastException>(script, settings, index: 30u);
68+
}
69+
}
70+
2371
[TestMethod]
2472
public void TestHasKeyWithDifferentHF()
2573
{
@@ -76,6 +124,16 @@ private static byte[] BuildHasKeyLargeIndexScript()
76124
}
77125

78126
private static void ExecuteAndAssertFault<TException>(byte[] script, ProtocolSettings settings, uint index) where TException : Exception
127+
{
128+
var engine = Execute(script, settings, index);
129+
130+
Assert.AreEqual(VMState.FAULT, engine.State, $"Expected FAULT at index={index}.");
131+
Assert.IsNotNull(engine.FaultException, $"Expected FaultException at index={index}.");
132+
Assert.IsInstanceOfType(engine.FaultException, typeof(TException),
133+
$"Expected {typeof(TException).Name} at index={index}, but got {engine.FaultException.GetType().Name}.");
134+
}
135+
136+
private static ApplicationEngine Execute(byte[] script, ProtocolSettings settings, uint index)
79137
{
80138
var snapshotCache = TestBlockchain.GetTestSnapshotCache();
81139
var block = new Block
@@ -102,10 +160,7 @@ private static void ExecuteAndAssertFault<TException>(byte[] script, ProtocolSet
102160
engine.LoadScript(script);
103161
engine.Execute();
104162

105-
Assert.AreEqual(VMState.FAULT, engine.State, $"Expected FAULT at index={index}.");
106-
Assert.IsNotNull(engine.FaultException, $"Expected FaultException at index={index}.");
107-
Assert.IsInstanceOfType(engine.FaultException, typeof(TException),
108-
$"Expected {typeof(TException).Name} at index={index}, but got {engine.FaultException.GetType().Name}.");
163+
return engine;
109164
}
110165
}
111166
}

0 commit comments

Comments
 (0)