Skip to content

Commit ca8aa4a

Browse files
committed
Issue python#15144: Fix possible integer overflow when handling pointers as integer values, by using Py_uintptr_t instead of size_t.
Patch by Serhiy Storchaka.
1 parent 1c47222 commit ca8aa4a

11 files changed

Lines changed: 44 additions & 45 deletions

File tree

‎Include/objimpl.h‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,9 @@ PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
181181
#endif
182182

183183
#define _PyObject_VAR_SIZE(typeobj, nitems) \
184-
(size_t) \
185-
( ( (typeobj)->tp_basicsize + \
186-
(nitems)*(typeobj)->tp_itemsize + \
187-
(SIZEOF_VOID_P - 1) \
188-
) & ~(SIZEOF_VOID_P - 1) \
189-
)
184+
_Py_SIZE_ROUND_UP((typeobj)->tp_basicsize + \
185+
(nitems)*(typeobj)->tp_itemsize, \
186+
SIZEOF_VOID_P)
190187

191188
#define PyObject_NEW(type, typeobj) \
192189
( (type *) PyObject_Init( \

‎Include/pymacro.h‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,18 @@
5252
#define PyDoc_STR(str) ""
5353
#endif
5454

55+
/* Below "a" is a power of 2. */
56+
/* Round down size "n" to be a multiple of "a". */
57+
#define _Py_SIZE_ROUND_DOWN(n, a) ((size_t)(n) & ~(size_t)((a) - 1))
58+
/* Round up size "n" to be a multiple of "a". */
59+
#define _Py_SIZE_ROUND_UP(n, a) (((size_t)(n) + \
60+
(size_t)((a) - 1)) & ~(size_t)((a) - 1))
61+
/* Round pointer "p" down to the closest "a"-aligned address <= "p". */
62+
#define _Py_ALIGN_DOWN(p, a) ((void *)((Py_uintptr_t)(p) & ~(Py_uintptr_t)((a) - 1)))
63+
/* Round pointer "p" up to the closest "a"-aligned address >= "p". */
64+
#define _Py_ALIGN_UP(p, a) ((void *)(((Py_uintptr_t)(p) + \
65+
(Py_uintptr_t)((a) - 1)) & ~(Py_uintptr_t)((a) - 1)))
66+
/* Check if pointer "p" is aligned to "a"-bytes boundary. */
67+
#define _Py_IS_ALIGNED(p, a) (!((Py_uintptr_t)(p) & (Py_uintptr_t)((a) - 1)))
68+
5569
#endif /* Py_PYMACRO_H */

‎Misc/NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ What's New in Python 3.3.1
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #15144: Fix possible integer overflow when handling pointers as
14+
integer values, by using Py_uintptr_t instead of size_t. Patch by
15+
Serhiy Storchaka.
16+
1317
- Issue #15965: Explicitly cast AT_FDCWD as (int). Required on Solaris 10
1418
(which defines AT_FDCWD as 0xffd19553), harmless on other platforms.
1519

‎Modules/_elementtree.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ do { memory -= size; printf("%8d - %s\n", memory, comment); } while (0)
9898
info. */
9999
#define JOIN_GET(p) ((Py_uintptr_t) (p) & 1)
100100
#define JOIN_SET(p, flag) ((void*) ((Py_uintptr_t) (JOIN_OBJ(p)) | (flag)))
101-
#define JOIN_OBJ(p) ((PyObject*) ((Py_uintptr_t) (p) & ~1))
101+
#define JOIN_OBJ(p) ((PyObject*) ((Py_uintptr_t) (p) & ~(Py_uintptr_t)1))
102102

103103
/* glue functions (see the init function for details) */
104104
static PyObject* elementtree_parseerror_obj;

‎Objects/obmalloc.c‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ static int running_on_valgrind = -1;
138138
*/
139139
#define ALIGNMENT 8 /* must be 2^N */
140140
#define ALIGNMENT_SHIFT 3
141-
#define ALIGNMENT_MASK (ALIGNMENT - 1)
142141

143142
/* Return the number of bytes in size class I, as a uint. */
144143
#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT)
@@ -314,14 +313,12 @@ struct arena_object {
314313
struct arena_object* prevarena;
315314
};
316315

317-
#undef ROUNDUP
318-
#define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK)
319-
#define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header))
316+
#define POOL_OVERHEAD _Py_SIZE_ROUND_UP(sizeof(struct pool_header), ALIGNMENT)
320317

321318
#define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */
322319

323320
/* Round pointer P down to the closest pool-aligned address <= P, as a poolp */
324-
#define POOL_ADDR(P) ((poolp)((uptr)(P) & ~(uptr)POOL_SIZE_MASK))
321+
#define POOL_ADDR(P) ((poolp)_Py_ALIGN_DOWN((P), POOL_SIZE))
325322

326323
/* Return total number of blocks in pool of size index I, as a uint. */
327324
#define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I))

‎Objects/stringlib/codecs.h‎

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
#if STRINGLIB_IS_UNICODE
44

5-
/* Mask to check or force alignment of a pointer to C 'long' boundaries */
6-
#define LONG_PTR_MASK (size_t) (SIZEOF_LONG - 1)
7-
85
/* Mask to quickly check whether a C 'long' contains a
96
non-ASCII, UTF8-encoded char. */
107
#if (SIZEOF_LONG == 8)
@@ -25,7 +22,7 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end,
2522
{
2623
Py_UCS4 ch;
2724
const char *s = *inptr;
28-
const char *aligned_end = (const char *) ((size_t) end & ~LONG_PTR_MASK);
25+
const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG);
2926
STRINGLIB_CHAR *p = dest + *outpos;
3027

3128
while (s < end) {
@@ -39,7 +36,7 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end,
3936
First, check if we can do an aligned read, as most CPUs have
4037
a penalty for unaligned reads.
4138
*/
42-
if (!((size_t) s & LONG_PTR_MASK)) {
39+
if (_Py_IS_ALIGNED(s, SIZEOF_LONG)) {
4340
/* Help register allocation */
4441
register const char *_s = s;
4542
register STRINGLIB_CHAR *_p = p;
@@ -453,7 +450,7 @@ STRINGLIB(utf16_decode)(const unsigned char **inptr, const unsigned char *e,
453450
{
454451
Py_UCS4 ch;
455452
const unsigned char *aligned_end =
456-
(const unsigned char *) ((size_t) e & ~LONG_PTR_MASK);
453+
(const unsigned char *) _Py_ALIGN_DOWN(e, SIZEOF_LONG);
457454
const unsigned char *q = *inptr;
458455
STRINGLIB_CHAR *p = dest + *outpos;
459456
/* Offsets from q for retrieving byte pairs in the right order. */
@@ -468,7 +465,7 @@ STRINGLIB(utf16_decode)(const unsigned char **inptr, const unsigned char *e,
468465
Py_UCS4 ch2;
469466
/* First check for possible aligned read of a C 'long'. Unaligned
470467
reads are more expensive, better to defer to another iteration. */
471-
if (!((size_t) q & LONG_PTR_MASK)) {
468+
if (_Py_IS_ALIGNED(q, SIZEOF_LONG)) {
472469
/* Fast path for runs of in-range non-surrogate chars. */
473470
register const unsigned char *_q = q;
474471
while (_q < aligned_end) {
@@ -565,7 +562,6 @@ STRINGLIB(utf16_decode)(const unsigned char **inptr, const unsigned char *e,
565562
#undef FAST_CHAR_MASK
566563
#undef STRIPPED_MASK
567564
#undef SWAB
568-
#undef LONG_PTR_MASK
569565

570566

571567
Py_LOCAL_INLINE(void)
@@ -588,7 +584,7 @@ STRINGLIB(utf16_encode)(unsigned short *out,
588584
_PyUnicode_CONVERT_BYTES(STRINGLIB_CHAR, unsigned short, in, end, out);
589585
# endif
590586
} else {
591-
const STRINGLIB_CHAR *unrolled_end = in + (len & ~ (Py_ssize_t) 3);
587+
const STRINGLIB_CHAR *unrolled_end = in + _Py_SIZE_ROUND_DOWN(len, 4);
592588
while (in < unrolled_end) {
593589
out[0] = SWAB2(in[0]);
594590
out[1] = SWAB2(in[1]);

‎Objects/stringlib/fastsearch.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ STRINGLIB(fastsearch_memchr_1char)(const STRINGLIB_CHAR* s, Py_ssize_t n,
4343

4444
#define DO_MEMCHR(memchr, s, needle, nchars) do { \
4545
candidate = memchr((const void *) (s), (needle), (nchars) * sizeof(STRINGLIB_CHAR)); \
46-
found = (const STRINGLIB_CHAR *) \
47-
((Py_ssize_t) candidate & (~ ((Py_ssize_t) sizeof(STRINGLIB_CHAR) - 1))); \
46+
found = (const STRINGLIB_CHAR *) _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR)); \
4847
} while (0)
4948

5049
if (mode == FAST_SEARCH) {

‎Objects/stringlib/find_max_char.h‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
#if STRINGLIB_IS_UNICODE
44

5-
/* Mask to check or force alignment of a pointer to C 'long' boundaries */
6-
#define LONG_PTR_MASK (size_t) (SIZEOF_LONG - 1)
7-
85
/* Mask to quickly check whether a C 'long' contains a
96
non-ASCII, UTF8-encoded char. */
107
#if (SIZEOF_LONG == 8)
@@ -21,10 +18,11 @@ Py_LOCAL_INLINE(Py_UCS4)
2118
STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end)
2219
{
2320
const unsigned char *p = (const unsigned char *) begin;
24-
const unsigned char *aligned_end = (const unsigned char *) ((size_t) end & ~LONG_PTR_MASK);
21+
const unsigned char *aligned_end =
22+
(const unsigned char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG);
2523

2624
while (p < end) {
27-
if (!((size_t) p & LONG_PTR_MASK)) {
25+
if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) {
2826
/* Help register allocation */
2927
register const unsigned char *_p = p;
3028
while (_p < aligned_end) {
@@ -43,7 +41,6 @@ STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end)
4341
return 127;
4442
}
4543

46-
#undef LONG_PTR_MASK
4744
#undef ASCII_CHAR_MASK
4845

4946
#else /* STRINGLIB_SIZEOF_CHAR == 1 */
@@ -72,7 +69,7 @@ STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end)
7269
register Py_UCS4 mask;
7370
Py_ssize_t n = end - begin;
7471
const STRINGLIB_CHAR *p = begin;
75-
const STRINGLIB_CHAR *unrolled_end = begin + (n & ~ (Py_ssize_t) 3);
72+
const STRINGLIB_CHAR *unrolled_end = begin + _Py_SIZE_ROUND_DOWN(n, 4);
7673
Py_UCS4 max_char;
7774

7875
max_char = MAX_CHAR_ASCII;

‎Objects/unicodeobject.c‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ extern "C" {
159159
const from_type *_end = (end); \
160160
Py_ssize_t n = (_end) - (_iter); \
161161
const from_type *_unrolled_end = \
162-
_iter + (n & ~ (Py_ssize_t) 3); \
162+
_iter + _Py_SIZE_ROUND_DOWN(n, 4); \
163163
while (_iter < (_unrolled_end)) { \
164164
_to[0] = (to_type) _iter[0]; \
165165
_to[1] = (to_type) _iter[1]; \
@@ -4635,9 +4635,6 @@ PyUnicode_DecodeUTF8(const char *s,
46354635
#include "stringlib/codecs.h"
46364636
#include "stringlib/undef.h"
46374637

4638-
/* Mask to check or force alignment of a pointer to C 'long' boundaries */
4639-
#define LONG_PTR_MASK (size_t) (SIZEOF_LONG - 1)
4640-
46414638
/* Mask to quickly check whether a C 'long' contains a
46424639
non-ASCII, UTF8-encoded char. */
46434640
#if (SIZEOF_LONG == 8)
@@ -4652,11 +4649,11 @@ static Py_ssize_t
46524649
ascii_decode(const char *start, const char *end, Py_UCS1 *dest)
46534650
{
46544651
const char *p = start;
4655-
const char *aligned_end = (const char *) ((size_t) end & ~LONG_PTR_MASK);
4652+
const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG);
46564653

46574654
#if SIZEOF_LONG <= SIZEOF_VOID_P
4658-
assert(!((size_t) dest & LONG_PTR_MASK));
4659-
if (!((size_t) p & LONG_PTR_MASK)) {
4655+
assert(_Py_IS_ALIGNED(dest, SIZEOF_LONG));
4656+
if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) {
46604657
/* Fast path, see in STRINGLIB(utf8_decode) for
46614658
an explanation. */
46624659
/* Help register allocation */
@@ -4682,7 +4679,7 @@ ascii_decode(const char *start, const char *end, Py_UCS1 *dest)
46824679
while (p < end) {
46834680
/* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h
46844681
for an explanation. */
4685-
if (!((size_t) p & LONG_PTR_MASK)) {
4682+
if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) {
46864683
/* Help register allocation */
46874684
register const char *_p = p;
46884685
while (_p < aligned_end) {
@@ -5390,7 +5387,7 @@ _PyUnicode_EncodeUTF16(PyObject *str,
53905387
return NULL;
53915388

53925389
/* output buffer is 2-bytes aligned */
5393-
assert(((Py_uintptr_t)PyBytes_AS_STRING(v) & 1) == 0);
5390+
assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(v), 2));
53945391
out = (unsigned short *)PyBytes_AS_STRING(v);
53955392
if (byteorder == 0)
53965393
*out++ = 0xFEFF;

‎Parser/node.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fancy_roundup(int n)
7171
* capacity. The code is tricky to avoid that.
7272
*/
7373
#define XXXROUNDUP(n) ((n) <= 1 ? (n) : \
74-
(n) <= 128 ? (((n) + 3) & ~3) : \
74+
(n) <= 128 ? _Py_SIZE_ROUND_UP((n), 4) : \
7575
fancy_roundup(n))
7676

7777

0 commit comments

Comments
 (0)