1 #include <string.h>
2 #include "pymemsets.h"
3 
4 /* Windows doesn't provide EOVERFLOW. */
5 #ifndef EOVERFLOW
6 #  define EOVERFLOW E2BIG
7 #endif
8 
9 
10 /* ISO C11 memset_s() function
11  *
12  * The function implements the best effort to overwrite a memory location
13  * with data in order to wipe sensitive information. memset() isn't
14  * sufficient because compilers often optimize a call to memset() away, when
15  * the memory segment is not accessed anymore, e.g. at the end of a function
16  * body. The functions follows recommendation MSC06-C of the `CERT Secure
17  * Coding Standards`.
18  *
19  * _Py_memset_s() comes WITHOUT warranty and does NOT guarantee any security.
20  * The page holding your data might already been swapped to disk or shared
21  * with a forked child process. It's still better than no wiping ...
22  *
23  * Section K.3.7.4.1, paragraph 4 [ISO/IEC 9899:2011] states:
24  *
25  *   The memset_s function copies the value of c (converted to an unsigned
26  *   char) into each of the first n characters of the object pointed to by s.
27  *   Unlike memset, any call to the memset_s function shall be evaluated
28  *   strictly according to the rules of the abstract machine as described
29  *   in (5.1.2.3). That is, any call to the memset_s function shall assume
30  *   that the memory indicated by s and n may be accessible in the future
31  *   and thus must contain the values indicated by c.
32  */
33 errno_t
_Py_memset_s(void * s,rsize_t smax,int c,rsize_t n)34 _Py_memset_s(void *s, rsize_t smax, int c, rsize_t n)
35 {
36     errno_t errval = 0;
37     volatile unsigned char *p = s;
38 
39     /* The 1st and 2nd runtime-constraint violation abort the function. */
40     if (s == NULL) {
41         errval = EINVAL;
42         goto err;
43     }
44     if (smax > RSIZE_MAX) {
45         errval = E2BIG;
46         goto err;
47     }
48     /* The 3rd and 4th runtime-constraint violation limit n to smax. */
49     if (n > RSIZE_MAX) {
50         errval = E2BIG;
51         n = smax;
52     }
53     if (n > smax) {
54         errval = EOVERFLOW;
55         n = smax;
56     }
57 
58     while (n--) {
59         *p++ = (unsigned char)c;
60     }
61 
62     if (errval == 0) {
63         return 0;
64     }
65     else {
66       err:
67         errno = errval;
68         return errval;
69     }
70 }
71 
72 /* I have seen other tricks to accomplish the same task faster with less CPU
73  * instructions.
74  *
75  * A patch for NetBSD creates a const volatile function
76  * pointer to memset() and calls memset() by dereferencing the volatile
77  * function pointer. I'm not sure if this works with all compilers.
78  *
79  *   static void * (* const volatile __memset_vp)(void *, int, size_t) = (memset);
80  *   (*__memset_vp)(s, c, n);
81  *
82  *   Source: http://ftp.netbsd.org/pub/NetBSD/misc/apb/memset_s.20120224.diff
83  *
84  * Another trick for GCC and LLVM is inline assembly to prevent dead code
85  * elimination:
86  *
87  *   memset(s, c, n);
88  *   asm volatile("" : : "r"(s) : "memory");
89  *        no code : no write : reads s : clobbers memory
90  *
91  *   Source: http://llvm.org/bugs/show_bug.cgi?id=15495
92  *
93  * Windows has SecureZeroMemory(void*, size_t).
94  * http://msdn.microsoft.com/en-us/library/windows/desktop/aa366877%28v=vs.85%29.aspx
95  */
96