Skip to content

Commit dbce53a

Browse files
authored
Merge branch 'main' into doc-regrtest-unittest-loader
2 parents cdcb8fb + 9d22a53 commit dbce53a

4 files changed

Lines changed: 87 additions & 68 deletions

File tree

‎Lib/test/test_bytes.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,15 @@ def test_fromhex(self):
518518
self.type2test.fromhex(data)
519519
self.assertIn('at position %s' % pos, str(cm.exception))
520520

521+
# gh-158583: Check for out of bounds reads (uninitialized bytes).
522+
# Create an array from a list to not overallocate.
523+
a = array.array('B', list(b'1234 ')) # Py_ISSPACE() loop
524+
self.assertEqual(self.type2test.fromhex(a), b'\x12\x34')
525+
526+
a = array.array('B', list(b'12345')) # Missing second digit
527+
with self.assertRaises(ValueError):
528+
self.type2test.fromhex(a)
529+
521530
def test_hex(self):
522531
self.assertRaises(TypeError, self.type2test.hex)
523532
self.assertRaises(TypeError, self.type2test.hex, 1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:meth:`bytes.fromhex` and :meth:`bytearray.fromhex`: Fix uninitialized
2+
memory read. Patch by Victor Stinner.

‎Modules/_io/bufferedio.c‎

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,109 +1205,115 @@ _io__Buffered_readinto1_impl(buffered *self, Py_buffer *buffer)
12051205
static PyObject *
12061206
_buffered_readline(buffered *self, Py_ssize_t limit)
12071207
{
1208-
PyObject *res = NULL;
1209-
PyObject *chunks = NULL;
1210-
Py_ssize_t n;
1211-
const char *start, *s, *end;
1208+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
12121209

12131210
CHECK_CLOSED(self, "readline of closed file")
12141211

12151212
/* First, try to find a line in the buffer. This can run unlocked because
12161213
the calls to the C API are simple enough that they can't trigger
12171214
any thread switch. */
1218-
n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t);
1219-
if (limit >= 0 && n > limit)
1215+
Py_ssize_t n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t);
1216+
if (limit >= 0 && n > limit) {
12201217
n = limit;
1221-
start = self->buffer + self->pos;
1222-
s = memchr(start, '\n', n);
1218+
}
1219+
const char *start = self->buffer + self->pos;
1220+
const char *s = memchr(start, '\n', n);
12231221
if (s != NULL) {
1224-
res = PyBytes_FromStringAndSize(start, s - start + 1);
1225-
if (res != NULL)
1226-
self->pos += s - start + 1;
1227-
goto end_unlocked;
1222+
n = s - start + 1;
1223+
PyObject *res = PyBytes_FromStringAndSize(start, n);
1224+
if (res == NULL) {
1225+
return NULL;
1226+
}
1227+
self->pos += n;
1228+
return res;
12281229
}
1230+
12291231
if (n == limit) {
1230-
res = PyBytes_FromStringAndSize(start, n);
1231-
if (res != NULL)
1232-
self->pos += n;
1233-
goto end_unlocked;
1232+
PyObject *res = PyBytes_FromStringAndSize(start, n);
1233+
if (res == NULL) {
1234+
return NULL;
1235+
}
1236+
self->pos += n;
1237+
return res;
12341238
}
12351239

1236-
if (!ENTER_BUFFERED(self))
1237-
goto end_unlocked;
1240+
PyBytesWriter *writer = NULL;
1241+
int locked = 0;
1242+
if (!ENTER_BUFFERED(self)) {
1243+
goto error;
1244+
}
1245+
locked = 1;
12381246

12391247
/* Now we try to get some more from the raw stream */
1240-
chunks = PyList_New(0);
1241-
if (chunks == NULL)
1242-
goto end;
1248+
writer = PyBytesWriter_Create(0);
1249+
if (writer == NULL) {
1250+
goto error;
1251+
}
1252+
12431253
if (n > 0) {
1244-
res = PyBytes_FromStringAndSize(start, n);
1245-
if (res == NULL)
1246-
goto end;
1247-
if (PyList_Append(chunks, res) < 0) {
1248-
Py_CLEAR(res);
1249-
goto end;
1254+
if (PyBytesWriter_WriteBytes(writer, start, n) < 0) {
1255+
goto error;
12501256
}
1251-
Py_CLEAR(res);
12521257
self->pos += n;
1253-
if (limit >= 0)
1258+
if (limit >= 0) {
12541259
limit -= n;
1260+
}
12551261
}
12561262
if (self->writable) {
1257-
PyObject *r = buffered_flush_and_rewind_unlocked(self);
1258-
if (r == NULL)
1259-
goto end;
1260-
Py_DECREF(r);
1263+
PyObject *res = buffered_flush_and_rewind_unlocked(self);
1264+
if (res == NULL) {
1265+
goto error;
1266+
}
1267+
Py_DECREF(res);
12611268
}
12621269

12631270
for (;;) {
12641271
_bufferedreader_reset_buf(self);
12651272
n = _bufferedreader_fill_buffer(self);
1266-
if (n == -1)
1267-
goto end;
1268-
if (n <= 0)
1273+
if (n == -1) {
1274+
goto error;
1275+
}
1276+
if (n <= 0) {
12691277
break;
1270-
if (limit >= 0 && n > limit)
1278+
}
1279+
if (limit >= 0 && n > limit) {
12711280
n = limit;
1281+
}
12721282
start = self->buffer;
1273-
end = start + n;
1283+
const char *end = start + n;
12741284
s = start;
12751285
while (s < end) {
12761286
if (*s++ == '\n') {
1277-
res = PyBytes_FromStringAndSize(start, s - start);
1278-
if (res == NULL)
1279-
goto end;
1287+
if (PyBytesWriter_WriteBytes(writer, start, s - start) < 0) {
1288+
goto error;
1289+
}
12801290
self->pos = s - start;
12811291
goto found;
12821292
}
12831293
}
1284-
res = PyBytes_FromStringAndSize(start, n);
1285-
if (res == NULL)
1286-
goto end;
1294+
1295+
if (PyBytesWriter_WriteBytes(writer, start, n) < 0) {
1296+
goto error;
1297+
}
12871298
if (n == limit) {
12881299
self->pos = n;
12891300
break;
12901301
}
1291-
if (PyList_Append(chunks, res) < 0) {
1292-
Py_CLEAR(res);
1293-
goto end;
1294-
}
1295-
Py_CLEAR(res);
1296-
if (limit >= 0)
1302+
if (limit >= 0) {
12971303
limit -= n;
1304+
}
12981305
}
1299-
found:
1300-
if (res != NULL && PyList_Append(chunks, res) < 0) {
1301-
Py_CLEAR(res);
1302-
goto end;
1303-
}
1304-
Py_XSETREF(res, PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks));
13051306

1306-
end:
1307+
found:
13071308
LEAVE_BUFFERED(self)
1308-
end_unlocked:
1309-
Py_XDECREF(chunks);
1310-
return res;
1309+
return PyBytesWriter_Finish(writer);
1310+
1311+
error:
1312+
PyBytesWriter_Discard(writer);
1313+
if (locked) {
1314+
LEAVE_BUFFERED(self)
1315+
}
1316+
return NULL;
13111317
}
13121318

13131319
/*[clinic input]

‎Objects/bytesobject.c‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,33 +2702,35 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray)
27022702
if (Py_ISSPACE(*str)) {
27032703
do {
27042704
str++;
2705+
if (str >= end) {
2706+
goto done;
2707+
}
27052708
} while (Py_ISSPACE(*str));
2706-
if (str >= end)
2707-
break;
27082709
}
27092710

27102711
top = _PyLong_DigitValue[*str];
27112712
if (top >= 16) {
27122713
invalid_char = str - start;
27132714
goto error;
27142715
}
2716+
27152717
str++;
2718+
if (str >= end) {
2719+
invalid_char = -1;
2720+
goto error;
2721+
}
27162722

27172723
bot = _PyLong_DigitValue[*str];
27182724
if (bot >= 16) {
2719-
/* Check if we had a second digit */
2720-
if (str >= end){
2721-
invalid_char = -1;
2722-
} else {
2723-
invalid_char = str - start;
2724-
}
2725+
invalid_char = str - start;
27252726
goto error;
27262727
}
27272728
str++;
27282729

27292730
*buf++ = (unsigned char)((top << 4) + bot);
27302731
}
27312732

2733+
done:
27322734
if (view.obj != NULL) {
27332735
PyBuffer_Release(&view);
27342736
}

0 commit comments

Comments
 (0)