Close #19568: Fix bytearray_setslice_linear(), fix handling of

PyByteArray_Resize() failure: leave the bytearray object in an consistent state.

If growth < 0, handling the memory allocation failure is tricky here because
the bytearray object has already been modified.  If lo != 0, the operation is
completed, but a MemoryError is still raised and the memory block is not
shrinked. If lo == 0, the bytearray is restored in its previous state and a
MemoryError is raised.
This commit is contained in:
Victor Stinner 2013-11-21 12:29:51 +01:00
parent 35f2803a24
commit 8455723cfb
1 changed files with 65 additions and 39 deletions

View File

@ -453,54 +453,80 @@ bytearray_setslice_linear(PyByteArrayObject *self,
Py_ssize_t avail = hi - lo;
char *buf = PyByteArray_AS_STRING(self);
Py_ssize_t growth = bytes_len - avail;
int res = 0;
assert(avail >= 0);
if (growth != 0) {
if (growth < 0) {
if (!_canresize(self))
return -1;
if (lo == 0) {
/* Shrink the buffer by advancing its logical start */
self->ob_start -= growth;
/*
0 lo hi old_size
| |<----avail----->|<-----tail------>|
| |<-bytes_len->|<-----tail------>|
0 new_lo new_hi new_size
*/
}
else {
/*
0 lo hi old_size
| |<----avail----->|<-----tomove------>|
| |<-bytes_len->|<-----tomove------>|
0 lo new_hi new_size
*/
memmove(buf + lo + bytes_len, buf + hi,
Py_SIZE(self) - hi);
}
}
/* XXX(nnorwitz): need to verify this can't overflow! */
if (PyByteArray_Resize(
(PyObject *)self, Py_SIZE(self) + growth) < 0)
if (growth < 0) {
if (!_canresize(self))
return -1;
buf = PyByteArray_AS_STRING(self);
if (growth > 0) {
/* Make the place for the additional bytes */
if (lo == 0) {
/* Shrink the buffer by advancing its logical start */
self->ob_start -= growth;
/*
0 lo hi old_size
| |<-avail->|<-----tomove------>|
| |<---bytes_len-->|<-----tomove------>|
0 lo new_hi new_size
*/
memmove(buf + lo + bytes_len, buf + hi,
Py_SIZE(self) - lo - bytes_len);
0 lo hi old_size
| |<----avail----->|<-----tail------>|
| |<-bytes_len->|<-----tail------>|
0 new_lo new_hi new_size
*/
}
else {
/*
0 lo hi old_size
| |<----avail----->|<-----tomove------>|
| |<-bytes_len->|<-----tomove------>|
0 lo new_hi new_size
*/
memmove(buf + lo + bytes_len, buf + hi,
Py_SIZE(self) - hi);
}
if (PyByteArray_Resize((PyObject *)self,
Py_SIZE(self) + growth) < 0) {
/* Issue #19578: Handling the memory allocation failure here is
tricky here because the bytearray object has already been
modified. Depending on growth and lo, the behaviour is
different.
If growth < 0 and lo != 0, the operation is completed, but a
MemoryError is still raised and the memory block is not
shrinked. Otherwise, the bytearray is restored in its previous
state and a MemoryError is raised. */
if (lo == 0) {
self->ob_start += growth;
return -1;
}
/* memmove() removed bytes, the bytearray object cannot be
restored in its previous state. */
Py_SIZE(self) += growth;
res = -1;
}
buf = PyByteArray_AS_STRING(self);
}
else if (growth > 0) {
if (Py_SIZE(self) > (Py_ssize_t)PY_SSIZE_T_MAX - growth) {
PyErr_NoMemory();
return -1;
}
if (PyByteArray_Resize((PyObject *)self,
Py_SIZE(self) + growth) < 0) {
return -1;
}
buf = PyByteArray_AS_STRING(self);
/* Make the place for the additional bytes */
/*
0 lo hi old_size
| |<-avail->|<-----tomove------>|
| |<---bytes_len-->|<-----tomove------>|
0 lo new_hi new_size
*/
memmove(buf + lo + bytes_len, buf + hi,
Py_SIZE(self) - lo - bytes_len);
}
if (bytes_len > 0)
memcpy(buf + lo, bytes, bytes_len);
return 0;
return res;
}
static int