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