Skip to content

Commit fa5d6a5

Browse files
committed
Issue python#22668: Ensure that format strings survive slicing after casting.
1 parent 2934262 commit fa5d6a5

File tree

3 files changed

+73
-6
lines changed

3 files changed

+73
-6
lines changed

Include/memoryobject.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ typedef struct {
4545
} _PyManagedBufferObject;
4646

4747

48-
/* static storage used for casting between formats */
48+
/* deprecated, removed in 3.5 */
4949
#define _Py_MEMORYVIEW_MAX_FORMAT 3 /* must be >= 3 */
5050

5151
/* memoryview state flags */
@@ -62,7 +62,7 @@ typedef struct {
6262
int flags; /* state flags */
6363
Py_ssize_t exports; /* number of buffer re-exports */
6464
Py_buffer view; /* private copy of the exporter's view */
65-
char format[_Py_MEMORYVIEW_MAX_FORMAT]; /* used for casting */
65+
char format[_Py_MEMORYVIEW_MAX_FORMAT]; /* deprecated, removed in 3.5 */
6666
PyObject *weakreflist;
6767
Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */
6868
} PyMemoryViewObject;

Lib/test/test_memoryview.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,25 @@ def test_reversed(self):
360360
self.assertEqual(list(reversed(m)), aslist)
361361
self.assertEqual(list(reversed(m)), list(m[::-1]))
362362

363+
def test_issue22668(self):
364+
m = memoryview(bytes(range(8)))
365+
b = m.cast('H')
366+
c = b[0:2]
367+
d = memoryview(b)
368+
369+
del b
370+
371+
self.assertEqual(c[0], 256)
372+
self.assertEqual(d[0], 256)
373+
self.assertEqual(c.format, "H")
374+
self.assertEqual(d.format, "H")
375+
376+
_ = m.cast('I')
377+
self.assertEqual(c[0], 256)
378+
self.assertEqual(d[0], 256)
379+
self.assertEqual(c.format, "H")
380+
self.assertEqual(d.format, "H")
381+
363382

364383
# Variations on source objects for the buffer: bytes-like objects, then arrays
365384
# with itemsize > 1.

Objects/memoryobject.c

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,51 @@ get_native_fmtchar(char *result, const char *fmt)
11351135
return -1;
11361136
}
11371137

1138+
Py_LOCAL_INLINE(char *)
1139+
get_native_fmtstr(const char *fmt)
1140+
{
1141+
int at = 0;
1142+
1143+
if (fmt[0] == '@') {
1144+
at = 1;
1145+
fmt++;
1146+
}
1147+
if (fmt[0] == '\0' || fmt[1] != '\0') {
1148+
return NULL;
1149+
}
1150+
1151+
#define RETURN(s) do { return at ? "@" s : s; } while (0)
1152+
1153+
switch (fmt[0]) {
1154+
case 'c': RETURN("c");
1155+
case 'b': RETURN("b");
1156+
case 'B': RETURN("B");
1157+
case 'h': RETURN("h");
1158+
case 'H': RETURN("H");
1159+
case 'i': RETURN("i");
1160+
case 'I': RETURN("I");
1161+
case 'l': RETURN("l");
1162+
case 'L': RETURN("L");
1163+
#ifdef HAVE_LONG_LONG
1164+
case 'q': RETURN("q");
1165+
case 'Q': RETURN("Q");
1166+
#endif
1167+
case 'n': RETURN("n");
1168+
case 'N': RETURN("N");
1169+
case 'f': RETURN("f");
1170+
case 'd': RETURN("d");
1171+
#ifdef HAVE_C99_BOOL
1172+
case '?': RETURN("?");
1173+
#else
1174+
case '?': RETURN("?");
1175+
#endif
1176+
case 'P': RETURN("P");
1177+
}
1178+
1179+
return NULL;
1180+
}
1181+
1182+
11381183
/* Cast a memoryview's data type to 'format'. The input array must be
11391184
C-contiguous. At least one of input-format, output-format must have
11401185
byte size. The output array is 1-D, with the same byte length as the
@@ -1184,10 +1229,13 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format)
11841229
goto out;
11851230
}
11861231

1187-
strncpy(mv->format, PyBytes_AS_STRING(asciifmt),
1188-
_Py_MEMORYVIEW_MAX_FORMAT);
1189-
mv->format[_Py_MEMORYVIEW_MAX_FORMAT-1] = '\0';
1190-
view->format = mv->format;
1232+
view->format = get_native_fmtstr(PyBytes_AS_STRING(asciifmt));
1233+
if (view->format == NULL) {
1234+
/* NOT_REACHED: get_native_fmtchar() already validates the format. */
1235+
PyErr_SetString(PyExc_RuntimeError,
1236+
"memoryview: internal error");
1237+
goto out;
1238+
}
11911239
view->itemsize = itemsize;
11921240

11931241
view->ndim = 1;

0 commit comments

Comments
 (0)