# HG changeset patch
# User Bob Ippolito <bob@redivi.com>
# Date 1403635766 25200
#      Tue Jun 24 11:49:26 2014 -0700
# Node ID 080ad34e4efca4f24d8d722d8831f48cec246bee
# Parent  b7cd1b0fd5bc78592ab7e7848704e8c2e5291889
Fix lower bound checking in scan_once / raw_decode API #98

diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+Version 3.5.3 released 2014-06-24
+
+* Fix lower bound checking in scan_once / raw_decode API
+  https://github.com/simplejson/simplejson/issues/98
+
 Version 3.5.2 released 2014-05-22
 
 * Fix Windows build with VS2008
diff --git a/conf.py b/conf.py
--- a/conf.py
+++ b/conf.py
@@ -44,7 +44,7 @@
 # The short X.Y version.
 version = '3.5'
 # The full version, including alpha/beta/rc tags.
-release = '3.5.2'
+release = '3.5.3'
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@
     DistutilsPlatformError
 
 IS_PYPY = hasattr(sys, 'pypy_translation_info')
-VERSION = '3.5.2'
+VERSION = '3.5.3'
 DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python"
 
 with open('README.rst', 'r') as f:
diff --git a/simplejson/__init__.py b/simplejson/__init__.py
--- a/simplejson/__init__.py
+++ b/simplejson/__init__.py
@@ -98,7 +98,7 @@
     Expecting property name: line 1 column 3 (char 2)
 """
 from __future__ import absolute_import
-__version__ = '3.5.2'
+__version__ = '3.5.3'
 __all__ = [
     'dump', 'dumps', 'load', 'loads',
     'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c
--- a/simplejson/_speedups.c
+++ b/simplejson/_speedups.c
@@ -2140,7 +2140,7 @@
     Py_ssize_t length = PyString_GET_SIZE(pystr);
     PyObject *rval = NULL;
     int fallthrough = 0;
-    if (idx >= length) {
+    if (idx < 0 || idx >= length) {
         raise_errmsg(ERR_EXPECTING_VALUE, pystr, idx);
         return NULL;
     }
@@ -2248,7 +2248,7 @@
     Py_ssize_t length = PyUnicode_GetLength(pystr);
     PyObject *rval = NULL;
     int fallthrough = 0;
-    if (idx >= length) {
+    if (idx < 0 || idx >= length) {
         raise_errmsg(ERR_EXPECTING_VALUE, pystr, idx);
         return NULL;
     }
diff --git a/simplejson/decoder.py b/simplejson/decoder.py
--- a/simplejson/decoder.py
+++ b/simplejson/decoder.py
@@ -384,6 +384,10 @@
         have extraneous data at the end.
 
         """
+        if idx < 0:
+            # Ensure that raw_decode bails on negative indexes, the regex
+            # would otherwise mask this behavior. #98
+            raise JSONDecodeError('Expecting value', s, idx)
         if _PY3 and not isinstance(s, text_type):
             raise TypeError("Input string must be text, not bytes")
         return self.scan_once(s, idx=_w(s, idx).end())
diff --git a/simplejson/scanner.py b/simplejson/scanner.py
--- a/simplejson/scanner.py
+++ b/simplejson/scanner.py
@@ -118,6 +118,11 @@
             raise JSONDecodeError(errmsg, string, idx)
 
     def scan_once(string, idx):
+        if idx < 0:
+            # Ensure the same behavior as the C speedup, otherwise
+            # this would work for *some* negative string indices due
+            # to the behavior of __getitem__ for strings. #98
+            raise JSONDecodeError('Expecting value', string, idx)
         try:
             return _scan_once(string, idx)
         finally:
diff --git a/simplejson/tests/test_decode.py b/simplejson/tests/test_decode.py
--- a/simplejson/tests/test_decode.py
+++ b/simplejson/tests/test_decode.py
@@ -86,3 +86,14 @@
         self.assertEqual(
             ({'a': {}}, 11),
             cls().raw_decode(" \n{\"a\": {}}"))
+
+    def test_bounds_checking(self):
+        # https://github.com/simplejson/simplejson/issues/98
+        j = json.decoder.JSONDecoder()
+        for i in [4, 5, 6, -1, -2, -3, -4, -5, -6]:
+            self.assertRaises(ValueError, j.scan_once, '1234', i)
+            self.assertRaises(ValueError, j.raw_decode, '1234', i)
+        x, y = sorted(['128931233', '472389423'], key=id)
+        diff = id(x) - id(y)
+        self.assertRaises(ValueError, j.scan_once, y, diff)
+        self.assertRaises(ValueError, j.raw_decode, y, i)