Skip to content

gh-96678: A few asserts to demonstrate undefined behaviour in 3.11 #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 3.11
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,11 +419,13 @@ get_ulonglong(PyObject *v, unsigned long long *p)
*/
#define GET_BITFIELD(v, size) \
if (NUM_BITS(size)) { \
assert(0 <= v); \
v <<= (sizeof(v)*8 - LOW_BIT(size) - NUM_BITS(size)); \
v >>= (sizeof(v)*8 - NUM_BITS(size)); \
}

/* This macro RETURNS the first parameter with the bit field CHANGED. */
/* This macro RETURNS the second parameter (x) with the bit field CHANGED. */
// TODO: this one has UB, but I can't quite figure out the assert.
#define SET(type, x, v, size) \
(NUM_BITS(size) ? \
( ( (type)x & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)v & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \
Expand Down Expand Up @@ -769,6 +771,15 @@ l_set(void *ptr, PyObject *value, Py_ssize_t size)
if (get_long(value, &val) < 0)
return NULL;
memcpy(&x, ptr, sizeof(x));
assert(
(NUM_BITS(size) - 1) < 63
);
assert(
NUM_BITS(size) ? (0 <= BIT_MASK(long, size)) : true
);

// TODO: figure out the proper assert here.
// Modules/_ctypes/cfield.c:789:9: runtime error: left shift of 1 by 63 places cannot be represented in type 'long'
x = SET(long, x, val, size);
memcpy(ptr, &x, sizeof(x));
_RET(value);
Expand Down
28 changes: 24 additions & 4 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,11 +812,16 @@ bu_int(_structmodulestate *state, const char *p, const formatdef *f)
Py_ssize_t i = f->size;
const unsigned char *bytes = (const unsigned char *)p;
do {
assert(0 <= x);
assert(x <= (LLONG_MAX>>8));
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be LONG_MAX.

x = (x<<8) | *bytes++;
} while (--i > 0);
/* Extend the sign bit. */
if (SIZEOF_LONG > f->size)
if (SIZEOF_LONG > f->size) {
assert(8 * f->size - 1 < 8 * SIZEOF_LONG_LONG - 1);
assert(0 <= 8 * f->size - 1);
x |= -(x & (1L << ((8 * f->size) - 1)));
}
return PyLong_FromLong(x);
}

Expand All @@ -839,11 +844,16 @@ bu_longlong(_structmodulestate *state, const char *p, const formatdef *f)
Py_ssize_t i = f->size;
const unsigned char *bytes = (const unsigned char *)p;
do {
assert(x >= 0);
assert(x <= (LLONG_MAX>>8));
x = (x<<8) | *bytes++;
} while (--i > 0);
/* Extend the sign bit. */
if (SIZEOF_LONG_LONG > f->size)
if (SIZEOF_LONG_LONG > f->size) {
assert(8 * f->size - 1 < 8 * SIZEOF_LONG_LONG - 1);
assert(0 <= 8 * f->size - 1);
x |= -(x & ((long long)1 << ((8 * f->size) - 1)));
}
return PyLong_FromLongLong(x);
}

Expand Down Expand Up @@ -1033,11 +1043,16 @@ lu_int(_structmodulestate *state, const char *p, const formatdef *f)
Py_ssize_t i = f->size;
const unsigned char *bytes = (const unsigned char *)p;
do {
assert(0 <= x);
assert(x <= (LONG_MAX>>8));
x = (x<<8) | bytes[--i];
} while (i > 0);
/* Extend the sign bit. */
if (SIZEOF_LONG > f->size)
if (SIZEOF_LONG > f->size) {
assert(8 * f->size - 1 < 8 * SIZEOF_LONG - 1);
assert(0 <= 8 * f->size - 1);
x |= -(x & (1L << ((8 * f->size) - 1)));
}
return PyLong_FromLong(x);
}

Expand All @@ -1060,11 +1075,16 @@ lu_longlong(_structmodulestate *state, const char *p, const formatdef *f)
Py_ssize_t i = f->size;
const unsigned char *bytes = (const unsigned char *)p;
do {
assert(0 <= x);
assert(x <= (LLONG_MAX>>8));
x = (x<<8) | bytes[--i];
} while (i > 0);
/* Extend the sign bit. */
if (SIZEOF_LONG_LONG > f->size)
if (SIZEOF_LONG_LONG > f->size) {
assert(8 * f->size - 1 < 8 * SIZEOF_LONG_LONG - 1);
assert(0 <= 8 * f->size - 1);
x |= -(x & ((long long)1 << ((8 * f->size) - 1)));
}
return PyLong_FromLongLong(x);
}

Expand Down
1 change: 1 addition & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5576,6 +5576,7 @@ meth_fastcall_keywords(PyObject* self, PyObject* const* args,
if (pyargs == NULL) {
return NULL;
}
assert(args != NULL);
PyObject *pykwargs = PyObject_Vectorcall((PyObject*)&PyDict_Type,
args + nargs, 0, kwargs);
return Py_BuildValue("NNN", _null_to_none(self), pyargs, pykwargs);
Expand Down
16 changes: 13 additions & 3 deletions Modules/audioop.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ st_14linear2ulaw(int16_t pcm_val) /* 2's complement (14-bit range) */
if (seg >= 8) /* out of range, return maximum value. */
return (unsigned char) (0x7F ^ mask);
else {
assert(0 <= seg);
uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
return (uval ^ mask);
}
Expand Down Expand Up @@ -346,11 +347,16 @@ static const int stepsizeTable[89] = {
SETINT32((cp), (i), (val)); \
} while(0)

static inline int lshift_safe(int x, Py_ssize_t b) {
assert(0 <= x);
assert(x <= (INT_MAX >> b));
return x << b;
}

#define GETSAMPLE32(size, cp, i) ( \
(size == 1) ? (int)GETINT8((cp), (i)) << 24 : \
(size == 2) ? (int)GETINT16((cp), (i)) << 16 : \
(size == 3) ? (int)GETINT24((cp), (i)) << 8 : \
(size == 1) ? lshift_safe((int)GETINT8((cp), (i)), 24) : \
(size == 2) ? lshift_safe((int)GETINT16((cp), (i)), 16) : \
(size == 3) ? lshift_safe((int)GETINT24((cp), (i)), 8) : \
(int)GETINT32((cp), (i)))

#define SETSAMPLE32(size, cp, i, val) do { \
Expand Down Expand Up @@ -1558,6 +1564,7 @@ audioop_ulaw2lin_impl(PyObject *module, Py_buffer *fragment, int width)

cp = fragment->buf;
for (i = 0; i < fragment->len*width; i += width) {
assert(0 <= st_ulaw2linear16(*cp));
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd also need to make sure that we don't left-shift the signed int too far.

Here and in the rest of this file.

int val = st_ulaw2linear16(*cp++) << 16;
SETSAMPLE32(width, ncp, i, val);
}
Expand Down Expand Up @@ -1632,6 +1639,7 @@ audioop_alaw2lin_impl(PyObject *module, Py_buffer *fragment, int width)
cp = fragment->buf;

for (i = 0; i < fragment->len*width; i += width) {
assert(0 <= st_alaw2linear16(*cp));
val = st_alaw2linear16(*cp++) << 16;
SETSAMPLE32(width, ncp, i, val);
}
Expand Down Expand Up @@ -1757,6 +1765,7 @@ audioop_lin2adpcm_impl(PyObject *module, Py_buffer *fragment, int width,

/* Step 6 - Output value */
if ( bufferstep ) {
assert (0 <= delta);
outputbuffer = (delta << 4) & 0xf0;
} else {
*ncp++ = (delta & 0x0f) | outputbuffer;
Expand Down Expand Up @@ -1875,6 +1884,7 @@ audioop_adpcm2lin_impl(PyObject *module, Py_buffer *fragment, int width,
step = stepsizeTable[index];

/* Step 6 - Output value */
assert(0 <= valpred);
SETSAMPLE32(width, ncp, i, valpred << 16);
}

Expand Down
1 change: 1 addition & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -6168,6 +6168,7 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
/* Pack other positional arguments into the *args argument */
if (co->co_flags & CO_VARARGS) {
PyObject *u = NULL;
assert(args != NULL);
u = _PyTuple_FromArraySteal(args + n, argcount - n);
if (u == NULL) {
goto fail_post_positional;
Expand Down