Skip to content

Commit 438e41d

Browse files
authored
Merge pull request #9 from secondlife/sl-18330-fix
SL-18830: Fix sporadic notation parse failure with very large input.
2 parents 8f5faba + 69c4397 commit 438e41d

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

llsd/base.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -465,20 +465,27 @@ def _peek(self, num=1, full=True):
465465
# off by 4 bytes to try to point the user at the right spot.
466466
self._error("Invalid length field %d" % num, -4)
467467

468-
got = self._stream.peek(num)
469-
if full and len(got) < num:
470-
# Going right to this error is a little iffy:
471-
# BufferedReader.peek() does not promise to return the requested
472-
# length, but does not clarify the conditions under which it
473-
# returns fewer bytes. If this is an actual problem, we could loop
474-
# until we have the requested length or EOF -- but the loop must
475-
# explicitly seek() past already-peeked data, then reset after.
476-
# https://docs.python.org/3/library/io.html#io.BufferedReader.peek
477-
self._error("Trying to peek past end of stream")
478-
479-
# Interestingly, peek() can also return MORE than requested -- but for
480-
# our purposes (e.g. ord(peek(1))) it's important to constrain it.
481-
return got[:num]
468+
# Instead of using self._stream.peek() at all, use read(num) and reset
469+
# the read pointer. BufferedReader.peek() does not promise to return
470+
# the requested length, but does not clarify the conditions under
471+
# which it returns fewer bytes.
472+
# https://docs.python.org/3/library/io.html#io.BufferedReader.peek
473+
# In practice, we've seen it fail with an input file up over 100Kb:
474+
# peek() returns only part of what we requested, but because we also
475+
# passed full=False (see LLSDNotationParser._get_re()), we didn't
476+
# notice and the parse failed looking for a map delimiter halfway
477+
# through a large decimal integer. read(num), on the other hand,
478+
# promises to return num bytes until actual EOF.
479+
oldpos = self._stream.tell()
480+
try:
481+
got = self._stream.read(num)
482+
if full and len(got) < num:
483+
self._error("Trying to peek past end of stream")
484+
485+
return got
486+
487+
finally:
488+
self._stream.seek(oldpos)
482489

483490
def _error(self, message, offset=0):
484491
oldpos = self._stream.tell()

0 commit comments

Comments
 (0)