26
26
#include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t()
27
27
#include "pycore_bytesobject.h" // _PyBytes_Find()
28
28
#include "pycore_fileutils.h" // _Py_stat_struct
29
+ #include "pycore_global_objects.h" // _Py_SINGLETON
30
+ #include "pycore_runtime.h" // _PyRuntime
29
31
30
32
#include <stddef.h> // offsetof()
31
33
#ifndef MS_WINDOWS
41
43
42
44
#ifdef MS_WINDOWS
43
45
#include <windows.h>
46
+ #include <ntsecapi.h> // LsaNtStatusToWinError
44
47
static int
45
48
my_getpagesize (void )
46
49
{
@@ -255,6 +258,11 @@ do { \
255
258
} while (0)
256
259
#endif /* UNIX */
257
260
261
+ // copied from bytesobject.c
262
+ #define CHARACTERS _Py_SINGLETON(bytes_characters)
263
+ #define CHARACTER (ch ) \
264
+ ((PyBytesObject *)&(CHARACTERS[ch]));
265
+
258
266
#if defined(MS_WIN32 ) && !defined(DONT_USE_SEH )
259
267
static DWORD
260
268
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) {
272
280
#if defined(MS_WIN32 ) && !defined(DONT_USE_SEH )
273
281
274
282
// 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.
275
285
if (count == 0 ) {
276
286
return 0 ;
277
287
}
@@ -282,7 +292,7 @@ safe_memcpy(void *restrict dest, const void *restrict src, size_t count) {
282
292
return 0 ;
283
293
}
284
294
__except (filter_page_exception (GetExceptionInformation (), & record )) {
285
- NTSTATUS status = record .ExceptionInformation [2 ];
295
+ NTSTATUS status = ( NTSTATUS ) record .ExceptionInformation [2 ];
286
296
ULONG code = LsaNtStatusToWinError (status );
287
297
PyErr_SetFromWindowsErr (code );
288
298
return -1 ;
@@ -293,6 +303,69 @@ safe_memcpy(void *restrict dest, const void *restrict src, size_t count) {
293
303
#endif
294
304
}
295
305
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
+
296
369
static PyObject *
297
370
mmap_read_byte_method (mmap_object * self ,
298
371
PyObject * Py_UNUSED (ignored ))
@@ -302,8 +375,8 @@ mmap_read_byte_method(mmap_object *self,
302
375
PyErr_SetString (PyExc_ValueError , "read byte out of range" );
303
376
return NULL ;
304
377
}
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 ) {
307
380
return NULL ;
308
381
}
309
382
else {
@@ -312,36 +385,67 @@ mmap_read_byte_method(mmap_object *self,
312
385
}
313
386
}
314
387
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
+
315
418
static PyObject *
316
419
mmap_read_line_method (mmap_object * self ,
317
420
PyObject * Py_UNUSED (ignored ))
318
421
{
319
422
Py_ssize_t remaining ;
320
423
char * start , * eol ;
321
- PyObject * result ;
322
424
323
425
CHECK_VALID (NULL );
324
426
325
427
remaining = (self -> pos < self -> size ) ? self -> size - self -> pos : 0 ;
326
428
if (!remaining )
327
429
return PyBytes_FromString ("" );
328
430
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
+
330
436
if (!eol )
331
437
eol = self -> data + self -> size ;
332
438
else
333
439
++ 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 );
337
442
}
338
443
339
444
static PyObject *
340
445
mmap_read_method (mmap_object * self ,
341
446
PyObject * args )
342
447
{
343
448
Py_ssize_t num_bytes = PY_SSIZE_T_MAX , remaining ;
344
- PyObject * result ;
345
449
346
450
CHECK_VALID (NULL );
347
451
if (!PyArg_ParseTuple (args , "|O&:read" , _Py_convert_optional_to_ssize_t , & num_bytes ))
@@ -353,17 +457,7 @@ mmap_read_method(mmap_object *self,
353
457
if (num_bytes < 0 || num_bytes > remaining )
354
458
num_bytes = remaining ;
355
459
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 );
367
461
}
368
462
369
463
static PyObject *
@@ -516,7 +610,7 @@ mmap_write_byte_method(mmap_object *self,
516
610
return NULL ;
517
611
}
518
612
519
- if (safe_memcpy (self -> data + self -> pos , value , 1 ) < 0 ) {
613
+ if (safe_byte_copy (self -> data + self -> pos , & value ) < 0 ) {
520
614
return NULL ;
521
615
}
522
616
else {
@@ -826,8 +920,9 @@ mmap_move_method(mmap_object *self, PyObject *args)
826
920
goto bounds ;
827
921
828
922
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
+ };
831
926
Py_RETURN_NONE ;
832
927
833
928
bounds :
@@ -1031,7 +1126,16 @@ mmap_item(mmap_object *self, Py_ssize_t i)
1031
1126
PyErr_SetString (PyExc_IndexError , "mmap index out of range" );
1032
1127
return NULL ;
1033
1128
}
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
+ }
1035
1139
}
1036
1140
1037
1141
static PyObject *
@@ -1068,17 +1172,16 @@ mmap_subscript(mmap_object *self, PyObject *item)
1068
1172
slicelen );
1069
1173
else {
1070
1174
char * result_buf = (char * )PyMem_Malloc (slicelen );
1071
- size_t cur ;
1072
- Py_ssize_t i ;
1073
1175
PyObject * result ;
1074
1176
1075
1177
if (result_buf == NULL )
1076
1178
return PyErr_NoMemory ();
1077
1179
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 ;
1081
1183
}
1184
+
1082
1185
result = PyBytes_FromStringAndSize (result_buf ,
1083
1186
slicelen );
1084
1187
PyMem_Free (result_buf );
@@ -1115,8 +1218,13 @@ mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
1115
1218
if (!is_writable (self ))
1116
1219
return -1 ;
1117
1220
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
+ }
1120
1228
}
1121
1229
1122
1230
static int
@@ -1160,8 +1268,13 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
1160
1268
return -1 ;
1161
1269
}
1162
1270
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
+ }
1165
1278
}
1166
1279
else if (PySlice_Check (item )) {
1167
1280
Py_ssize_t start , stop , step , slicelen ;
@@ -1186,24 +1299,21 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
1186
1299
}
1187
1300
1188
1301
CHECK_VALID_OR_RELEASE (-1 , vbuf );
1302
+ int result = 0 ;
1189
1303
if (slicelen == 0 ) {
1190
1304
}
1191
1305
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
+ }
1193
1309
}
1194
1310
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 ;
1203
1313
}
1204
1314
}
1205
1315
PyBuffer_Release (& vbuf );
1206
- return 0 ;
1316
+ return result ;
1207
1317
}
1208
1318
else {
1209
1319
PyErr_SetString (PyExc_TypeError ,
0 commit comments