Skip to content
This repository was archived by the owner on Oct 13, 2025. It is now read-only.

Commit edaff75

Browse files
committed
Fixes infinite loop bugs in text parsing of lobs and strings.
1 parent 9d416b5 commit edaff75

File tree

2 files changed

+219
-12
lines changed

2 files changed

+219
-12
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using Amazon.IonDotnet.Internals.Text;
5+
using Microsoft.VisualStudio.TestTools.UnitTesting;
6+
7+
namespace Amazon.IonDotnet.Tests.Internals
8+
{
9+
[TestClass]
10+
public class TextScannerTest
11+
{
12+
private TextScanner CreateScanner(string input)
13+
{
14+
var bytes = System.Text.Encoding.UTF8.GetBytes(input);
15+
var stream = new MemoryStream(bytes);
16+
var textStream = new UnicodeStream(stream);
17+
return new TextScanner(textStream);
18+
}
19+
20+
[TestMethod]
21+
public void TestMalformedBlobHandling()
22+
{
23+
// Test simple malformed blob
24+
var scanner = CreateScanner("{{");
25+
Assert.ThrowsException<UnexpectedEofException>(() =>
26+
{
27+
scanner.NextToken();
28+
while (scanner.Token != TextConstants.TokenEof)
29+
{
30+
scanner.NextToken();
31+
}
32+
});
33+
34+
// Test the specific malformed input that caused the infinite loop
35+
var hexString = "282f5959595959595959593a3a282b2727357b7b7b7b7b7b7b7b27272728fb2b272829";
36+
var bytes = new byte[hexString.Length / 2];
37+
for (int i = 0; i < bytes.Length; i++)
38+
{
39+
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
40+
}
41+
42+
var stream = new MemoryStream(bytes);
43+
var textStream = new UnicodeStream(stream);
44+
scanner = new TextScanner(textStream);
45+
46+
Assert.ThrowsException<UnexpectedEofException>(() =>
47+
{
48+
scanner.NextToken();
49+
while (scanner.Token != TextConstants.TokenEof)
50+
{
51+
scanner.NextToken();
52+
}
53+
});
54+
}
55+
56+
[TestMethod]
57+
public void TestSingleQuotedStringEofHandling()
58+
{
59+
// Test EOF in single-quoted string
60+
var scanner = CreateScanner("'unterminated");
61+
Assert.ThrowsException<UnexpectedEofException>(() =>
62+
{
63+
scanner.NextToken();
64+
while (scanner.Token != TextConstants.TokenEof)
65+
{
66+
scanner.NextToken();
67+
}
68+
});
69+
70+
// Test EOF after escape in single-quoted string
71+
scanner = CreateScanner("'escaped\\");
72+
Assert.ThrowsException<UnexpectedEofException>(() =>
73+
{
74+
scanner.NextToken();
75+
while (scanner.Token != TextConstants.TokenEof)
76+
{
77+
scanner.NextToken();
78+
}
79+
});
80+
}
81+
82+
[TestMethod]
83+
public void TestTripleQuotedStringEofHandling()
84+
{
85+
// Test EOF in triple-quoted string
86+
var scanner = CreateScanner("'''unterminated");
87+
Assert.ThrowsException<UnexpectedEofException>(() =>
88+
{
89+
scanner.NextToken();
90+
while (scanner.Token != TextConstants.TokenEof)
91+
{
92+
scanner.NextToken();
93+
}
94+
});
95+
96+
// Test EOF after partial triple quote
97+
scanner = CreateScanner("'''content''");
98+
Assert.ThrowsException<UnexpectedEofException>(() =>
99+
{
100+
scanner.NextToken();
101+
while (scanner.Token != TextConstants.TokenEof)
102+
{
103+
scanner.NextToken();
104+
}
105+
});
106+
107+
// Test EOF after escape in triple-quoted string
108+
scanner = CreateScanner("'''escaped\\");
109+
Assert.ThrowsException<UnexpectedEofException>(() =>
110+
{
111+
scanner.NextToken();
112+
while (scanner.Token != TextConstants.TokenEof)
113+
{
114+
scanner.NextToken();
115+
}
116+
});
117+
}
118+
119+
[TestMethod]
120+
public void TestDoubleQuotedStringEofHandling()
121+
{
122+
// Test EOF in double-quoted string
123+
var scanner = CreateScanner("\"unterminated");
124+
Assert.ThrowsException<UnexpectedEofException>(() =>
125+
{
126+
scanner.NextToken();
127+
while (scanner.Token != TextConstants.TokenEof)
128+
{
129+
scanner.NextToken();
130+
}
131+
});
132+
133+
// Test EOF after escape in double-quoted string
134+
scanner = CreateScanner("\"escaped\\");
135+
Assert.ThrowsException<UnexpectedEofException>(() =>
136+
{
137+
scanner.NextToken();
138+
while (scanner.Token != TextConstants.TokenEof)
139+
{
140+
scanner.NextToken();
141+
}
142+
});
143+
}
144+
145+
[TestMethod]
146+
public void TestMalformedClobHandling()
147+
{
148+
// Test malformed clob with missing closing braces
149+
var scanner = CreateScanner("{{\"clob content\"");
150+
Assert.ThrowsException<UnexpectedEofException>(() =>
151+
{
152+
scanner.NextToken();
153+
while (scanner.Token != TextConstants.TokenEof)
154+
{
155+
scanner.NextToken();
156+
}
157+
});
158+
159+
// Test malformed clob with triple quotes
160+
scanner = CreateScanner("{{{'''clob content");
161+
Assert.ThrowsException<UnexpectedEofException>(() =>
162+
{
163+
scanner.NextToken();
164+
while (scanner.Token != TextConstants.TokenEof)
165+
{
166+
scanner.NextToken();
167+
}
168+
});
169+
}
170+
}
171+
}

Amazon.IonDotnet/Internals/Text/TextScanner.cs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public void LoadBlob(StringBuilder sb)
202202
var c = this.SkipOverWhiteSpace(CommentStrategy.Break);
203203
while (true)
204204
{
205-
if (c == TextConstants.TokenEof)
205+
if (c == CharacterSequence.CharSeqEof)
206206
{
207207
throw new UnexpectedEofException();
208208
}
@@ -217,7 +217,7 @@ public void LoadBlob(StringBuilder sb)
217217
}
218218

219219
c = this.ReadChar();
220-
if (c == TextConstants.TokenEof)
220+
if (c == CharacterSequence.CharSeqEof)
221221
{
222222
throw new UnexpectedEofException();
223223
}
@@ -1006,7 +1006,7 @@ private void SkipOverBlob()
10061006
var c = this.SkipOverWhiteSpace(CommentStrategy.Break);
10071007
while (true)
10081008
{
1009-
if (c == TextConstants.TokenEof)
1009+
if (c == CharacterSequence.CharSeqEof)
10101010
{
10111011
throw new UnexpectedEofException();
10121012
}
@@ -1020,7 +1020,8 @@ private void SkipOverBlob()
10201020
}
10211021

10221022
c = this.ReadChar();
1023-
if (c == TextConstants.TokenEof)
1023+
1024+
if (c == CharacterSequence.CharSeqEof)
10241025
{
10251026
throw new UnexpectedEofException();
10261027
}
@@ -1038,21 +1039,40 @@ private void SkipTripleQuotedString(CommentStrategy commentStrategy)
10381039
var c = this.ReadChar();
10391040
switch (c)
10401041
{
1041-
case -1:
1042+
case CharacterSequence.CharSeqEof:
10421043
throw new UnexpectedEofException();
10431044
case '\\':
1044-
this.ReadChar();
1045+
var escaped = this.ReadChar();
1046+
if (escaped == CharacterSequence.CharSeqEof)
1047+
{
1048+
throw new UnexpectedEofException();
1049+
}
1050+
10451051
break;
10461052
case '\'':
10471053
c = this.ReadChar();
1054+
if (c == CharacterSequence.CharSeqEof)
1055+
{
1056+
throw new UnexpectedEofException();
1057+
}
10481058

10491059
// the 2nd '
10501060
if (c == '\'')
10511061
{
10521062
c = this.ReadChar();
1063+
if (c == CharacterSequence.CharSeqEof)
1064+
{
1065+
throw new UnexpectedEofException();
1066+
}
10531067

10541068
// the 3rd
1055-
if (c == this.ReadChar())
1069+
var next = this.ReadChar();
1070+
if (next == CharacterSequence.CharSeqEof)
1071+
{
1072+
throw new UnexpectedEofException();
1073+
}
1074+
1075+
if (c == next)
10561076
{
10571077
c = this.SkipOverWhiteSpace(commentStrategy);
10581078
if (c == '\'' && this.Is2SingleQuotes())
@@ -1515,12 +1535,23 @@ private int SkipSingleQuotedString()
15151535
var c = this.ReadStringChar(Characters.ProhibitionContext.None);
15161536
switch (c)
15171537
{
1518-
case -1:
1538+
case CharacterSequence.CharSeqEof:
15191539
throw new UnexpectedEofException();
15201540
case '\'':
1521-
return this.ReadChar();
1541+
var next = this.ReadChar();
1542+
if (next == CharacterSequence.CharSeqEof)
1543+
{
1544+
throw new UnexpectedEofException();
1545+
}
1546+
1547+
return next;
15221548
case '\\':
1523-
this.ReadChar();
1549+
var escaped = this.ReadChar();
1550+
if (escaped == CharacterSequence.CharSeqEof)
1551+
{
1552+
throw new UnexpectedEofException();
1553+
}
1554+
15241555
break;
15251556
}
15261557
}
@@ -1537,7 +1568,7 @@ private void SkipDoubleQuotedString()
15371568
var c = this.ReadStringChar(Characters.ProhibitionContext.None);
15381569
switch (c)
15391570
{
1540-
case -1:
1571+
case CharacterSequence.CharSeqEof:
15411572
throw new UnexpectedEofException();
15421573
case CharacterSequence.CharSeqEscapedNewlineSequence1:
15431574
case CharacterSequence.CharSeqEscapedNewlineSequence2:
@@ -1547,7 +1578,12 @@ private void SkipDoubleQuotedString()
15471578
case '"':
15481579
return;
15491580
case '\\':
1550-
this.ReadChar();
1581+
var escaped = this.ReadChar();
1582+
if (escaped == CharacterSequence.CharSeqEof)
1583+
{
1584+
throw new UnexpectedEofException();
1585+
}
1586+
15511587
break;
15521588
}
15531589
}

0 commit comments

Comments
 (0)