diff --git a/Include/internal/pycore_abstract.h b/Include/internal/pycore_abstract.h index 3cc0afac4bd5b4..0d2c2bb9242c19 100644 --- a/Include/internal/pycore_abstract.h +++ b/Include/internal/pycore_abstract.h @@ -8,6 +8,23 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +// Return 1 if 0 <= index < limit and 0 otherwise +// The argument limit should be non-negative +static inline int _Py_is_valid_index(Py_ssize_t index, Py_ssize_t limit) +{ + /* The cast to size_t lets us use just a single comparison + to check whether i is in the range: 0 <= i < limit. + See: Section 14.2 "Bounds Checking" in the Agner Fog + optimization manual found at: + https://www.agner.org/optimize/optimizing_cpp.pdf + + The function is not affected by -fwrapv, -fno-wrapv and -ftrapv + compiler options of GCC and clang + */ + assert(limit >= 0); + return (size_t)index < (size_t)limit; +} + // Fast inlined version of PyIndex_Check() static inline int _PyIndex_Check(PyObject *obj) diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index b97ade6126fa08..20ccfe19114821 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -8,6 +8,7 @@ #endif #include "Python.h" +#include "pycore_abstract.h" // _Py_is_valid_index() #include "pycore_bytesobject.h" // _PyBytes_Repeat #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_ceval.h" // _PyEval_GetBuiltin() @@ -835,7 +836,7 @@ array_length(arrayobject *a) static PyObject * array_item(arrayobject *a, Py_ssize_t i) { - if (i < 0 || i >= Py_SIZE(a)) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetString(PyExc_IndexError, "array index out of range"); return NULL; } @@ -997,7 +998,7 @@ array_del_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh) static int array_ass_item(arrayobject *a, Py_ssize_t i, PyObject *v) { - if (i < 0 || i >= Py_SIZE(a)) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetString(PyExc_IndexError, "array assignment index out of range"); return -1; @@ -1280,7 +1281,7 @@ array_array_pop_impl(arrayobject *self, Py_ssize_t i) } if (i < 0) i += Py_SIZE(self); - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "pop index out of range"); return NULL; } @@ -2470,7 +2471,7 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) return -1; if (i < 0) i += Py_SIZE(self); - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "array assignment index out of range"); return -1; diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 66ed0b8efb775c..34e375cfb82049 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -23,7 +23,7 @@ #endif #include -#include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() +#include "pycore_abstract.h" // _Py_is_valid_index(), _Py_convert_optional_to_ssize_t() #include "pycore_bytesobject.h" // _PyBytes_Find() #include "pycore_fileutils.h" // _Py_stat_struct @@ -960,7 +960,7 @@ static PyObject * mmap_item(mmap_object *self, Py_ssize_t i) { CHECK_VALID(NULL); - if (i < 0 || i >= self->size) { + if (!_Py_is_valid_index(i, self->size)) { PyErr_SetString(PyExc_IndexError, "mmap index out of range"); return NULL; } @@ -977,7 +977,7 @@ mmap_subscript(mmap_object *self, PyObject *item) return NULL; if (i < 0) i += self->size; - if (i < 0 || i >= self->size) { + if (!_Py_is_valid_index(i, self->size)) { PyErr_SetString(PyExc_IndexError, "mmap index out of range"); return NULL; @@ -1031,7 +1031,7 @@ mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v) const char *buf; CHECK_VALID(-1); - if (i < 0 || i >= self->size) { + if (!_Py_is_valid_index(i, self->size)) { PyErr_SetString(PyExc_IndexError, "mmap index out of range"); return -1; } @@ -1068,7 +1068,7 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value) return -1; if (i < 0) i += self->size; - if (i < 0 || i >= self->size) { + if (!_Py_is_valid_index(i, self->size)) { PyErr_SetString(PyExc_IndexError, "mmap index out of range"); return -1; diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 659de7d3dd5a99..2d51c18a55e2c1 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -359,7 +359,7 @@ bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) static PyObject * bytearray_getitem(PyByteArrayObject *self, Py_ssize_t i) { - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return NULL; } @@ -378,7 +378,7 @@ bytearray_subscript(PyByteArrayObject *self, PyObject *index) if (i < 0) i += PyByteArray_GET_SIZE(self); - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return NULL; } @@ -573,7 +573,7 @@ bytearray_setitem(PyByteArrayObject *self, Py_ssize_t i, PyObject *value) i += Py_SIZE(self); } - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return -1; } @@ -613,7 +613,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu i += PyByteArray_GET_SIZE(self); } - if (i < 0 || i >= Py_SIZE(self)) { + if (!_Py_is_valid_index(i, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return -1; } @@ -1810,7 +1810,7 @@ bytearray_pop_impl(PyByteArrayObject *self, Py_ssize_t index) } if (index < 0) index += Py_SIZE(self); - if (index < 0 || index >= Py_SIZE(self)) { + if (!_Py_is_valid_index(index, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "pop index out of range"); return NULL; } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 26227dd251122d..75ea9025c78c73 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1495,7 +1495,7 @@ bytes_contains(PyObject *self, PyObject *arg) static PyObject * bytes_item(PyBytesObject *a, Py_ssize_t i) { - if (i < 0 || i >= Py_SIZE(a)) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; } @@ -1602,7 +1602,7 @@ bytes_subscript(PyBytesObject* self, PyObject* item) return NULL; if (i < 0) i += PyBytes_GET_SIZE(self); - if (i < 0 || i >= PyBytes_GET_SIZE(self)) { + if (!_Py_is_valid_index(i, PyBytes_GET_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; diff --git a/Objects/codeobject.c b/Objects/codeobject.c index dc46b773c26528..05f87c64e3a90c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2,7 +2,8 @@ #include "Python.h" #include "opcode.h" - +#include "structmember.h" // PyMemberDef +#include "pycore_abstract.h" // _Py_is_valid_index() #include "pycore_code.h" // _PyCodeConstructor #include "pycore_frame.h" // FRAME_SPECIALS_SIZE #include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs @@ -1382,8 +1383,7 @@ PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!PyCode_Check(code) || index < 0 || - index >= interp->co_extra_user_count) { + if (!PyCode_Check(code) || !_Py_is_valid_index(index, interp->co_extra_user_count)) { PyErr_BadInternalCall(); return -1; } diff --git a/Objects/listobject.c b/Objects/listobject.c index 2d04218439bd20..e1e3d5de77fcf6 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -229,18 +229,6 @@ PyList_Size(PyObject *op) return Py_SIZE(op); } -static inline int -valid_index(Py_ssize_t i, Py_ssize_t limit) -{ - /* The cast to size_t lets us use just a single comparison - to check whether i is in the range: 0 <= i < limit. - - See: Section 14.2 "Bounds Checking" in the Agner Fog - optimization manual found at: - https://www.agner.org/optimize/optimizing_cpp.pdf - */ - return (size_t) i < (size_t) limit; -} PyObject * PyList_GetItem(PyObject *op, Py_ssize_t i) @@ -249,7 +237,7 @@ PyList_GetItem(PyObject *op, Py_ssize_t i) PyErr_BadInternalCall(); return NULL; } - if (!valid_index(i, Py_SIZE(op))) { + if (!_Py_is_valid_index(i, Py_SIZE(op))) { _Py_DECLARE_STR(list_err, "list index out of range"); PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; @@ -267,7 +255,7 @@ PyList_SetItem(PyObject *op, Py_ssize_t i, PyErr_BadInternalCall(); return -1; } - if (!valid_index(i, Py_SIZE(op))) { + if (!_Py_is_valid_index(i, Py_SIZE(op))) { Py_XDECREF(newitem); PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); @@ -458,7 +446,7 @@ list_contains(PyListObject *a, PyObject *el) static PyObject * list_item(PyListObject *a, Py_ssize_t i) { - if (!valid_index(i, Py_SIZE(a))) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err)); return NULL; } @@ -778,7 +766,7 @@ list_inplace_repeat(PyListObject *self, Py_ssize_t n) static int list_ass_item(PyListObject *a, Py_ssize_t i, PyObject *v) { - if (!valid_index(i, Py_SIZE(a))) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); return -1; @@ -1077,7 +1065,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) } if (index < 0) index += Py_SIZE(self); - if (!valid_index(index, Py_SIZE(self))) { + if (!_Py_is_valid_index(index, Py_SIZE(self))) { PyErr_SetString(PyExc_IndexError, "pop index out of range"); return NULL; } @@ -2670,9 +2658,8 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start, } if (stop < 0) { stop += Py_SIZE(self); - if (stop < 0) - stop = 0; } + for (i = start; i < stop && i < Py_SIZE(self); i++) { PyObject *obj = self->ob_item[i]; Py_INCREF(obj); diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index 6a38952fdc1f3b..5e07d0d235ed2b 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2378,7 +2378,7 @@ lookup_dimension(const Py_buffer *view, char *ptr, int dim, Py_ssize_t index) if (index < 0) { index += nitems; } - if (index < 0 || index >= nitems) { + if (!_Py_is_valid_index(index, nitems)) { PyErr_Format(PyExc_IndexError, "index out of bounds on dimension %d", dim + 1); return NULL; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index d567839c5e3a0b..cb5eee3526442f 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -102,7 +102,7 @@ PyTuple_GetItem(PyObject *op, Py_ssize_t i) PyErr_BadInternalCall(); return NULL; } - if (i < 0 || i >= Py_SIZE(op)) { + if (!_Py_is_valid_index(i, Py_SIZE(op))) { PyErr_SetString(PyExc_IndexError, "tuple index out of range"); return NULL; } @@ -118,7 +118,7 @@ PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem) PyErr_BadInternalCall(); return -1; } - if (i < 0 || i >= Py_SIZE(op)) { + if (!_Py_is_valid_index(i, Py_SIZE(op))) { Py_XDECREF(newitem); PyErr_SetString(PyExc_IndexError, "tuple assignment index out of range"); @@ -363,7 +363,7 @@ tuplecontains(PyTupleObject *a, PyObject *el) static PyObject * tupleitem(PyTupleObject *a, Py_ssize_t i) { - if (i < 0 || i >= Py_SIZE(a)) { + if (!_Py_is_valid_index(i, Py_SIZE(a))) { PyErr_SetString(PyExc_IndexError, "tuple index out of range"); return NULL; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 836e14fd5d5dea..54672c603be06c 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1487,11 +1487,11 @@ PyUnicode_CopyCharacters(PyObject *to, Py_ssize_t to_start, return -1; } - if ((size_t)from_start > (size_t)PyUnicode_GET_LENGTH(from)) { + if (!_Py_is_valid_index(from_start, PyUnicode_GET_LENGTH(from))) { PyErr_SetString(PyExc_IndexError, "string index out of range"); return -1; } - if ((size_t)to_start > (size_t)PyUnicode_GET_LENGTH(to)) { + if (!_Py_is_valid_index(to_start, PyUnicode_GET_LENGTH(to))) { PyErr_SetString(PyExc_IndexError, "string index out of range"); return -1; } @@ -3895,7 +3895,7 @@ PyUnicode_ReadChar(PyObject *unicode, Py_ssize_t index) PyErr_BadArgument(); return (Py_UCS4)-1; } - if (index < 0 || index >= PyUnicode_GET_LENGTH(unicode)) { + if (!_Py_is_valid_index(index, PyUnicode_GET_LENGTH(unicode))) { PyErr_SetString(PyExc_IndexError, "string index out of range"); return (Py_UCS4)-1; }