Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0bd57b

Browse files
committed
add safe_byte_copy, safe_memmove, safe_memchr, safe_copy_from_slice and safe_copy_to_slice
handle SEH in mmap_read_line_method, mmap_item, mmap_ass_item and mmap_subscript, mmap_ass_subscript, mmap_move_method
1 parent 7983869 commit c0bd57b

File tree

1 file changed

+153
-43
lines changed

1 file changed

+153
-43
lines changed

Modules/mmapmodule.c

Lines changed: 153 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t()
2727
#include "pycore_bytesobject.h" // _PyBytes_Find()
2828
#include "pycore_fileutils.h" // _Py_stat_struct
29+
#include "pycore_global_objects.h"// _Py_SINGLETON
30+
#include "pycore_runtime.h" // _PyRuntime
2931

3032
#include <stddef.h> // offsetof()
3133
#ifndef MS_WINDOWS
@@ -41,6 +43,7 @@
4143

4244
#ifdef MS_WINDOWS
4345
#include <windows.h>
46+
#include <ntsecapi.h> // LsaNtStatusToWinError
4447
static int
4548
my_getpagesize(void)
4649
{
@@ -255,6 +258,11 @@ do { \
255258
} while (0)
256259
#endif /* UNIX */
257260

261+
// copied from bytesobject.c
262+
#define CHARACTERS _Py_SINGLETON(bytes_characters)
263+
#define CHARACTER(ch) \
264+
((PyBytesObject *)&(CHARACTERS[ch]));
265+
258266
#if defined(MS_WIN32) && !defined(DONT_USE_SEH)
259267
static DWORD
260268
filter_page_exception(EXCEPTION_POINTERS *ptrs, EXCEPTION_RECORD *record)
@@ -272,6 +280,8 @@ safe_memcpy(void *restrict dest, const void *restrict src, size_t count) {
272280
#if defined(MS_WIN32) && !defined(DONT_USE_SEH)
273281

274282
// never fail for count 0
283+
// according to https://en.cppreference.com/w/cpp/string/byte/memcpy
284+
// If either dest or src is an invalid or null pointer, the behavior is undefined, even if count is zero.
275285
if (count == 0) {
276286
return 0;
277287
}
@@ -282,7 +292,7 @@ safe_memcpy(void *restrict dest, const void *restrict src, size_t count) {
282292
return 0;
283293
}
284294
__except (filter_page_exception(GetExceptionInformation(), &record)) {
285-
NTSTATUS status = record.ExceptionInformation[2];
295+
NTSTATUS status = (NTSTATUS) record.ExceptionInformation[2];
286296
ULONG code = LsaNtStatusToWinError(status);
287297
PyErr_SetFromWindowsErr(code);
288298
return -1;
@@ -293,6 +303,69 @@ safe_memcpy(void *restrict dest, const void *restrict src, size_t count) {
293303
#endif
294304
}
295305

306+
#if defined(MS_WIN32) && !defined(DONT_USE_SEH)
307+
#define handle_invalid_mem(sourcecode) \
308+
EXCEPTION_RECORD record; \
309+
__try { \
310+
sourcecode \
311+
return 0; \
312+
} \
313+
__except (filter_page_exception(GetExceptionInformation(), &record)) { \
314+
NTSTATUS status = (NTSTATUS) record.ExceptionInformation[2]; \
315+
ULONG code = LsaNtStatusToWinError(status); \
316+
PyErr_SetFromWindowsErr(code); \
317+
return -1; \
318+
}
319+
#else
320+
#define handle_invalid_mem(sourcecode) \
321+
sourcecode \
322+
return 0;
323+
#endif
324+
325+
int
326+
safe_byte_copy(char *dest, const char *src) {
327+
handle_invalid_mem(
328+
*dest = *src;
329+
)
330+
}
331+
332+
int
333+
safe_memchr(void **out, const void *ptr, int ch, size_t count) {
334+
handle_invalid_mem(
335+
*out = memchr(ptr, ch, count);
336+
)
337+
}
338+
339+
int
340+
safe_memmove(void *dest, const void *src, size_t count) {
341+
handle_invalid_mem(
342+
memmove(dest, src, count);
343+
)
344+
}
345+
346+
int
347+
safe_copy_from_slice(char *dest, const char *src, Py_ssize_t start, Py_ssize_t step, Py_ssize_t slicelen) {
348+
handle_invalid_mem(
349+
size_t cur;
350+
Py_ssize_t i;
351+
for (cur = start, i = 0; i < slicelen; cur += step, i++) {
352+
dest[cur] = src[i];
353+
}
354+
)
355+
}
356+
357+
int
358+
safe_copy_to_slice(char *dest, const char *src, Py_ssize_t start, Py_ssize_t step, Py_ssize_t slicelen) {
359+
handle_invalid_mem(
360+
size_t cur;
361+
Py_ssize_t i;
362+
for (cur = start, i = 0; i < slicelen; cur += step, i++) {
363+
dest[i] = src[cur];
364+
}
365+
)
366+
}
367+
368+
296369
static PyObject *
297370
mmap_read_byte_method(mmap_object *self,
298371
PyObject *Py_UNUSED(ignored))
@@ -302,8 +375,8 @@ mmap_read_byte_method(mmap_object *self,
302375
PyErr_SetString(PyExc_ValueError, "read byte out of range");
303376
return NULL;
304377
}
305-
unsigned char dest;
306-
if (safe_memcpy(&dest, self->data + self->pos, 1) < 0) {
378+
char dest;
379+
if (safe_byte_copy(&dest, self->data + self->pos) < 0) {
307380
return NULL;
308381
}
309382
else {
@@ -312,36 +385,67 @@ mmap_read_byte_method(mmap_object *self,
312385
}
313386
}
314387

388+
PyObject *
389+
_read_mmap_mem(mmap_object *self, char *start, size_t num_bytes) {
390+
if (num_bytes == 1) {
391+
char dest;
392+
if (safe_byte_copy(&dest, start) < 0) {
393+
return NULL;
394+
}
395+
else {
396+
PyBytesObject *op = CHARACTER(dest & 255);
397+
assert(_Py_IsImmortal(op));
398+
self->pos += 1;
399+
return (PyObject *)op;
400+
}
401+
}
402+
else {
403+
PyObject *result = PyBytes_FromStringAndSize(NULL, num_bytes);
404+
// this check was not done previously in mmap_read_line_method, which means pos was increased even in case of error
405+
if (result == NULL) {
406+
return NULL;
407+
}
408+
if (safe_memcpy(PyBytes_AS_STRING(result), start, num_bytes) < 0) {
409+
Py_CLEAR(result);
410+
}
411+
else {
412+
self->pos += num_bytes;
413+
}
414+
return result;
415+
}
416+
}
417+
315418
static PyObject *
316419
mmap_read_line_method(mmap_object *self,
317420
PyObject *Py_UNUSED(ignored))
318421
{
319422
Py_ssize_t remaining;
320423
char *start, *eol;
321-
PyObject *result;
322424

323425
CHECK_VALID(NULL);
324426

325427
remaining = (self->pos < self->size) ? self->size - self->pos : 0;
326428
if (!remaining)
327429
return PyBytes_FromString("");
328430
start = self->data + self->pos;
329-
eol = memchr(start, '\n', remaining);
431+
432+
if (safe_memchr(&eol, start, '\n', remaining) < 0) {
433+
return NULL;
434+
}
435+
330436
if (!eol)
331437
eol = self->data + self->size;
332438
else
333439
++eol; /* advance past newline */
334-
result = PyBytes_FromStringAndSize(start, (eol - start));
335-
self->pos += (eol - start);
336-
return result;
440+
441+
return _read_mmap_mem(self, start, eol - start);
337442
}
338443

339444
static PyObject *
340445
mmap_read_method(mmap_object *self,
341446
PyObject *args)
342447
{
343448
Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
344-
PyObject *result;
345449

346450
CHECK_VALID(NULL);
347451
if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
@@ -353,17 +457,7 @@ mmap_read_method(mmap_object *self,
353457
if (num_bytes < 0 || num_bytes > remaining)
354458
num_bytes = remaining;
355459

356-
result = PyBytes_FromStringAndSize(NULL, num_bytes);
357-
if (result == NULL) {
358-
return NULL;
359-
}
360-
if (safe_memcpy(PyBytes_AS_STRING(result), self->data + self->pos, num_bytes) < 0) {
361-
Py_CLEAR(result);
362-
}
363-
else {
364-
self->pos += num_bytes;
365-
}
366-
return result;
460+
return _read_mmap_mem(self, self->data + self->pos, num_bytes);
367461
}
368462

369463
static PyObject *
@@ -516,7 +610,7 @@ mmap_write_byte_method(mmap_object *self,
516610
return NULL;
517611
}
518612

519-
if (safe_memcpy(self->data + self->pos, value, 1) < 0) {
613+
if (safe_byte_copy(self->data + self->pos, &value) < 0) {
520614
return NULL;
521615
}
522616
else {
@@ -826,8 +920,9 @@ mmap_move_method(mmap_object *self, PyObject *args)
826920
goto bounds;
827921

828922
CHECK_VALID(NULL);
829-
memmove(&self->data[dest], &self->data[src], cnt);
830-
923+
if (safe_memmove(self->data + dest, self->data + src, cnt) < 0) {
924+
return NULL;
925+
};
831926
Py_RETURN_NONE;
832927

833928
bounds:
@@ -1031,7 +1126,16 @@ mmap_item(mmap_object *self, Py_ssize_t i)
10311126
PyErr_SetString(PyExc_IndexError, "mmap index out of range");
10321127
return NULL;
10331128
}
1034-
return PyBytes_FromStringAndSize(self->data + i, 1);
1129+
1130+
char dest;
1131+
if (safe_byte_copy(&dest, self->data + i) < 0) {
1132+
return NULL;
1133+
}
1134+
else {
1135+
PyBytesObject *op = CHARACTER(dest & 255);
1136+
assert(_Py_IsImmortal(op));
1137+
return (PyObject *)op;
1138+
}
10351139
}
10361140

10371141
static PyObject *
@@ -1068,17 +1172,16 @@ mmap_subscript(mmap_object *self, PyObject *item)
10681172
slicelen);
10691173
else {
10701174
char *result_buf = (char *)PyMem_Malloc(slicelen);
1071-
size_t cur;
1072-
Py_ssize_t i;
10731175
PyObject *result;
10741176

10751177
if (result_buf == NULL)
10761178
return PyErr_NoMemory();
10771179

1078-
for (cur = start, i = 0; i < slicelen;
1079-
cur += step, i++) {
1080-
result_buf[i] = self->data[cur];
1180+
if (safe_copy_to_slice(result_buf, self->data, start, step, slicelen) < 0) {
1181+
PyMem_Free(result_buf);
1182+
return NULL;
10811183
}
1184+
10821185
result = PyBytes_FromStringAndSize(result_buf,
10831186
slicelen);
10841187
PyMem_Free(result_buf);
@@ -1115,8 +1218,13 @@ mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
11151218
if (!is_writable(self))
11161219
return -1;
11171220
buf = PyBytes_AsString(v);
1118-
self->data[i] = buf[0];
1119-
return 0;
1221+
1222+
if (safe_byte_copy(self->data + i, buf) < 0) {
1223+
return -1;
1224+
}
1225+
else {
1226+
return 0;
1227+
}
11201228
}
11211229

11221230
static int
@@ -1160,8 +1268,13 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
11601268
return -1;
11611269
}
11621270
CHECK_VALID(-1);
1163-
self->data[i] = (char) v;
1164-
return 0;
1271+
1272+
if (safe_byte_copy(self->data + i, (char *) &v) < 0) {
1273+
return -1;
1274+
}
1275+
else {
1276+
return 0;
1277+
}
11651278
}
11661279
else if (PySlice_Check(item)) {
11671280
Py_ssize_t start, stop, step, slicelen;
@@ -1186,24 +1299,21 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
11861299
}
11871300

11881301
CHECK_VALID_OR_RELEASE(-1, vbuf);
1302+
int result = 0;
11891303
if (slicelen == 0) {
11901304
}
11911305
else if (step == 1) {
1192-
memcpy(self->data + start, vbuf.buf, slicelen);
1306+
if (safe_memcpy(self->data + start, vbuf.buf, slicelen) < 0) {
1307+
result = -1;
1308+
}
11931309
}
11941310
else {
1195-
size_t cur;
1196-
Py_ssize_t i;
1197-
1198-
for (cur = start, i = 0;
1199-
i < slicelen;
1200-
cur += step, i++)
1201-
{
1202-
self->data[cur] = ((char *)vbuf.buf)[i];
1311+
if (safe_copy_from_slice(self->data, (char *)vbuf.buf, start, step, slicelen) < 0) {
1312+
result = -1;
12031313
}
12041314
}
12051315
PyBuffer_Release(&vbuf);
1206-
return 0;
1316+
return result;
12071317
}
12081318
else {
12091319
PyErr_SetString(PyExc_TypeError,

0 commit comments

Comments
 (0)