Skip to content

Commit 53f020b

Browse files
paulirwinclaude
andauthored
Fix integer overflow in IndexInputStream.Read(), #1158 (#1173)
Fix integer overflow in IndexInputStream.Read(), #1158 When input.Length - input.Position exceeds int.MaxValue, casting to int causes overflow resulting in negative values. This fix uses long for the remaining calculation before safely casting to int via Math.Min. Added unit test TestGitHubIssue1158_IndexInputStream_Read_IntegerOverflow to demonstrate and verify the fix. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f38de47 commit 53f020b

2 files changed

Lines changed: 131 additions & 2 deletions

File tree

src/Lucene.Net.Replicator/IndexInputInputStream.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ public override void SetLength(long value)
6565

6666
public override int Read(byte[] buffer, int offset, int count)
6767
{
68-
int remaining = (int)(input.Length - input.Position); // LUCENENET specific: Renamed from getFilePointer() to match FileStream
69-
int readCount = Math.Min(remaining, count);
68+
// LUCENENET specific: This method is quite different than the Java version, to match the Stream semantics.
69+
long remaining = input.Length - input.Position; // LUCENENET NOTE: using 64-bit math to avoid overflow. Be careful when porting changes.
70+
int readCount = (int)Math.Min(remaining, count);
7071
input.ReadBytes(buffer, offset, readCount);
7172
return readCount;
7273
}

src/Lucene.Net.Tests.Replicator/IndexInputStreamTest.cs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Lucene.Net.Attributes;
22
using Lucene.Net.Index;
33
using Lucene.Net.Replicator;
4+
using Lucene.Net.Store;
45
using Lucene.Net.Util;
56
using NUnit.Framework;
67
using System;
@@ -63,6 +64,133 @@ public void Read_RemainingIndexInputLargerThanReadCount_ReturnsExpectedSection([
6364
Assert.AreEqual(readBuffer, buffer.Skip((section - 1) * readBytes).Take(readBytes).ToArray());
6465
}
6566

67+
/// <summary>
68+
/// Test for GitHub issue #1158: Integer overflow in IndexInputStream.Read()
69+
/// https://github.com/apache/lucenenet/issues/1158
70+
/// </summary>
71+
[Test]
72+
[LuceneNetSpecific]
73+
public void TestGitHubIssue1158_IndexInputStream_Read_IntegerOverflow()
74+
{
75+
// This test verifies the fix for the integer overflow bug in IndexInputStream.Read()
76+
// Bug: When input.Length - input.Position > int.MaxValue, casting to int causes overflow
77+
// Previously on line 68: int remaining = (int)(input.Length - input.Position);
78+
79+
// Arrange: Create a mock IndexInput that simulates a very large file
80+
var largeIndexInput = new LargeFileMockIndexInput();
81+
var stream = new IndexInputStream(largeIndexInput);
82+
83+
// Position the stream at the beginning (position = 0)
84+
// Length is int.MaxValue + 1000L, so (Length - Position) = int.MaxValue + 1000L
85+
// When cast to int, this overflows and becomes negative: -2147482649
86+
largeIndexInput.Seek(0);
87+
88+
// Act: Try to read from the stream
89+
byte[] buffer = new byte[50];
90+
91+
// The bug manifests here:
92+
// remaining = (int)(int.MaxValue + 1000L) = -2147482649 (negative due to overflow)
93+
// readCount = Math.Min(-2147482649, 50) = -2147482649 (negative)
94+
// This would cause ReadBytes to be called with negative length
95+
96+
try
97+
{
98+
int bytesRead = stream.Read(buffer, 0, buffer.Length);
99+
100+
// If we get here without exception, check if the read was successful
101+
// With the bug, bytesRead might be negative or cause other issues
102+
Assert.IsTrue(bytesRead >= 0, $"BytesRead should not be negative, but was {bytesRead}");
103+
Assert.AreEqual(50, bytesRead, "Should read exactly 50 bytes");
104+
}
105+
catch (ArgumentException ex)
106+
{
107+
// The bug may cause an ArgumentException when ReadBytes is called with negative length
108+
Assert.Fail($"Integer overflow caused ArgumentException: {ex.Message}");
109+
}
110+
}
111+
112+
}
113+
114+
/// <summary>
115+
/// Mock IndexInput that simulates a file larger than int.MaxValue
116+
/// to test for integer overflow issues
117+
/// </summary>
118+
internal class LargeFileMockIndexInput : IndexInput
119+
{
120+
private long position = 0;
121+
private readonly long length = (long)int.MaxValue + 1000L;
122+
123+
public LargeFileMockIndexInput() : base("LargeFileMockIndexInput")
124+
{
125+
}
126+
127+
public override long Length => length;
128+
129+
public override long Position => position;
130+
131+
public override byte ReadByte()
132+
{
133+
if (position >= length)
134+
throw new System.IO.EndOfStreamException();
135+
position++;
136+
return 0;
137+
}
138+
139+
public override void ReadBytes(byte[] b, int offset, int len)
140+
{
141+
// Validate parameters to prevent unexpected behavior
142+
if (b == null)
143+
{
144+
throw new ArgumentNullException(nameof(b));
145+
}
146+
147+
if (offset < 0)
148+
{
149+
throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be negative");
150+
}
151+
152+
if (len < 0)
153+
{
154+
throw new ArgumentOutOfRangeException(nameof(len), "Length cannot be negative");
155+
}
156+
157+
if (offset + len > b.Length)
158+
{
159+
throw new ArgumentException("The sum of offset and length exceeds the buffer length");
160+
}
161+
162+
long available = length - position;
163+
if (available < len)
164+
{
165+
throw new System.IO.EndOfStreamException();
166+
}
167+
// Simulate reading by advancing position
168+
position += len;
169+
// Fill buffer with dummy data
170+
for (int i = 0; i < len; i++)
171+
{
172+
b[offset + i] = 0;
173+
}
174+
}
175+
176+
public override void Seek(long pos)
177+
{
178+
if (pos < 0 || pos > length)
179+
throw new ArgumentOutOfRangeException(nameof(pos));
180+
position = pos;
181+
}
182+
183+
public override object Clone()
184+
{
185+
var clone = new LargeFileMockIndexInput();
186+
clone.position = this.position;
187+
return clone;
188+
}
189+
190+
protected override void Dispose(bool disposing)
191+
{
192+
// No resources to dispose
193+
}
66194
}
67195

68196
//Note: LUCENENET specific

0 commit comments

Comments
 (0)