Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
secure_crt.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * partial implementation of VC8's secure CRT functions
25  */
26 
27 #include "precompiled.h"
28 
29 #include <stdio.h>
30 #include <errno.h>
31 #include <stdarg.h>
32 
33 #include "lib/secure_crt.h"
34 
35 // we were included from wsecure_crt.cpp; skip all stuff that
36 // must only be done once.
37 #ifndef WSECURE_CRT
39  { ERR::STRING_NOT_TERMINATED, L"Invalid string (no 0 terminator found in buffer)" }
40 };
41 STATUS_ADD_DEFINITIONS(secureCrtStatusDefinitions);
42 #endif
43 
44 
45 // written against http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n1031.pdf .
46 // optimized for size - e.g. strcpy calls strncpy with n = SIZE_MAX.
47 
48 // since char and wide versions of these functions are basically the same,
49 // this source file implements generic versions and bridges the differences
50 // with these macros. wsecure_crt.cpp #defines WSECURE_CRT and
51 // includes this file.
52 
53 // Note: These defines are all #undef:ed at the end of the file - remember to
54 // add a corresponding #undef when adding a #define.
55 #ifdef WSECURE_CRT
56 # define tchar wchar_t
57 # define T(string_literal) L ## string_literal
58 # define tnlen wcsnlen
59 # define tncpy_s wcsncpy_s
60 # define tcpy_s wcscpy_s
61 # define tncat_s wcsncat_s
62 # define tcat_s wcscat_s
63 # define tcmp wcscmp
64 # define tcpy wcscpy
65 # define tvsnprintf vswprintf // used by implementation
66 # define tvsprintf_s vswprintf_s
67 # define tsprintf_s swprintf_s
68 #else
69 # define tchar char
70 # define T(string_literal) string_literal
71 # define tnlen strnlen
72 # define tncpy_s strncpy_s
73 # define tcpy_s strcpy_s
74 # define tncat_s strncat_s
75 # define tcat_s strcat_s
76 # define tcmp strcmp
77 # define tcpy strcpy
78 # define tvsnprintf vsnprintf // used by implementation
79 # define tvsprintf_s vsprintf_s
80 # define tsprintf_s sprintf_s
81 #endif // #ifdef WSECURE_CRT
82 
83 
84 // return <retval> and raise an assertion if <condition> doesn't hold.
85 // usable as a statement.
86 #define ENFORCE(condition, err_to_warn, retval) STMT(\
87  if(!(condition)) \
88  { \
89  DEBUG_WARN_ERR(err_to_warn); \
90  return retval; \
91  } \
92 )
93 
94 // raise a debug warning if <len> is the size of a pointer.
95 // catches bugs such as: tchar* s = ..; tcpy_s(s, sizeof(s), T(".."));
96 // if warnings get annoying, replace with debug_printf. usable as a statement.
97 //
98 // currently disabled due to high risk of false positives.
99 #define WARN_IF_PTR_LEN(len)\
100 /*
101  ENSURE(len != sizeof(char*));
102 */
103 
104 
105 // skip our implementation if already available, but not the
106 // self-test and the t* defines (needed for test).
107 #if EMULATE_SECURE_CRT
108 
109 #if !OS_UNIX || OS_MACOSX || OS_OPENBSD
110 // return length [in characters] of a string, not including the trailing
111 // null character. to protect against access violations, only the
112 // first <max_len> characters are examined; if the null character is
113 // not encountered by then, <max_len> is returned.
114 size_t tnlen(const tchar* str, size_t max_len)
115 {
116  // note: we can't bail - what would the return value be?
117  ENSURE(str != 0);
118 
119  WARN_IF_PTR_LEN(max_len);
120 
121  size_t len;
122  for(len = 0; len < max_len; len++)
123  if(*str++ == '\0')
124  break;
125 
126  return len;
127 }
128 #endif // !OS_UNIX
129 
130 
131 // copy at most <max_src_chars> (not including trailing null) from
132 // <src> into <dst>, which must not overlap.
133 // if thereby <max_dst_chars> (including null) would be exceeded,
134 // <dst> is set to the empty string and ERANGE returned; otherwise,
135 // 0 is returned to indicate success and that <dst> is null-terminated.
136 //
137 // note: padding with zeroes is not called for by N1031.
138 int tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)
139 {
140  // the MS implementation returns EINVAL and allows dst = 0 if
141  // max_dst_chars = max_src_chars = 0. no mention of this in
142  // 3.6.2.1.1, so don't emulate that behavior.
143  ENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);
144  ENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL); // N1031 says ERANGE, MSDN/MSVC says EINVAL
145  *dst = '\0'; // in case src ENFORCE is triggered
146  ENFORCE(src != 0, ERR::INVALID_POINTER, EINVAL);
147 
148  WARN_IF_PTR_LEN(max_dst_chars);
149  WARN_IF_PTR_LEN(max_src_chars);
150 
151  // copy string until null character encountered or limit reached.
152  // optimized for size (less comparisons than MS impl) and
153  // speed (due to well-predicted jumps; we don't bother unrolling).
154  tchar* p = dst;
155  size_t chars_left = std::min(max_dst_chars, max_src_chars);
156  while(chars_left != 0)
157  {
158  // success: reached end of string normally.
159  if((*p++ = *src++) == '\0')
160  return 0;
161  chars_left--;
162  }
163 
164  // which limit did we hit?
165  // .. dst, and last character wasn't null: overflow.
166  if(max_dst_chars <= max_src_chars)
167  {
168  *dst = '\0';
169  ENFORCE(0, ERR::INVALID_SIZE, ERANGE);
170  }
171  // .. source: success, but still need to null-terminate the destination.
172  *p = '\0';
173  return 0;
174 }
175 
176 
177 // copy <src> (including trailing null) into <dst>, which must not overlap.
178 // if thereby <max_dst_chars> (including null) would be exceeded,
179 // <dst> is set to the empty string and ERANGE returned; otherwise,
180 // 0 is returned to indicate success and that <dst> is null-terminated.
181 int tcpy_s(tchar* dst, size_t max_dst_chars, const tchar* src)
182 {
183  return tncpy_s(dst, max_dst_chars, src, SIZE_MAX);
184 }
185 
186 
187 // append <src> to <dst>, which must not overlap.
188 // if thereby <max_dst_chars> (including null) would be exceeded,
189 // <dst> is set to the empty string and ERANGE returned; otherwise,
190 // 0 is returned to indicate success and that <dst> is null-terminated.
191 int tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)
192 {
193  ENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);
194  ENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL); // N1031 says ERANGE, MSDN/MSVC says EINVAL
195  // src is checked in tncpy_s
196 
197  // WARN_IF_PTR_LEN not necessary: both max_dst_chars and max_src_chars
198  // are checked by tnlen / tncpy_s (respectively).
199 
200  const size_t dst_len = tnlen(dst, max_dst_chars);
201  if(dst_len == max_dst_chars)
202  {
203  *dst = '\0';
204  ENFORCE(0, ERR::STRING_NOT_TERMINATED, EINVAL); // N1031/MSDN says ERANGE, MSVC says EINVAL
205  }
206 
207  tchar* const end = dst+dst_len;
208  const size_t chars_left = max_dst_chars-dst_len;
209  int ret = tncpy_s(end, chars_left, src, max_src_chars);
210  // if tncpy_s overflowed, we need to clear the start of our string
211  // (not just the appended part). can't do that by default, because
212  // the beginning of dst is not changed in normal operation.
213  if(ret != 0)
214  *dst = '\0';
215  return ret;
216 }
217 
218 
219 // append <src> to <dst>, which must not overlap.
220 // if thereby <max_dst_chars> (including null) would be exceeded,
221 // <dst> is set to the empty string and ERANGE returned; otherwise,
222 // 0 is returned to indicate success and that <dst> is null-terminated.
223 //
224 // note: implemented as tncat_s(dst, max_dst_chars, src, SIZE_MAX)
225 int tcat_s(tchar* dst, size_t max_dst_chars, const tchar* src)
226 {
227  return tncat_s(dst, max_dst_chars, src, SIZE_MAX);
228 }
229 
230 
231 int tvsprintf_s(tchar* dst, size_t max_dst_chars, const tchar* fmt, va_list ap)
232 {
233  if(!dst || !fmt || max_dst_chars == 0)
234  {
235  errno = EINVAL;
236  return -1;
237  }
238 
239  const int ret = tvsnprintf(dst, max_dst_chars, fmt, ap);
240  if(ret >= int(max_dst_chars)) // not enough space
241  {
242  dst[0] = '\0';
243  return -1;
244  }
245  return ret; // negative if error, else # chars written (excluding '\0')
246 }
247 
248 
249 int tsprintf_s(tchar* buf, size_t max_chars, const tchar* fmt, ...)
250 {
251  va_list ap;
252  va_start(ap, fmt);
253  int len = tvsprintf_s(buf, max_chars, fmt, ap);
254  va_end(ap);
255  return len;
256 }
257 
258 #endif // #if EMULATE_SECURE_CRT
259 
260 #undef tchar
261 #undef T
262 #undef tnlen
263 #undef tncpy_s
264 #undef tcpy_s
265 #undef tncat_s
266 #undef tcat_s
267 #undef tcmp
268 #undef tcpy
269 #undef tvsnprintf
270 #undef tvsprintf_s
271 #undef tsprintf_s
#define tnlen
Definition: secure_crt.cpp:71
#define tncat_s
Definition: secure_crt.cpp:74
#define tcpy_s
Definition: secure_crt.cpp:73
const Status STRING_NOT_TERMINATED
Definition: secure_crt.h:36
#define ENFORCE(condition, err_to_warn, retval)
Definition: secure_crt.cpp:86
#define SIZE_MAX
Definition: posix_types.h:57
#define tcat_s
Definition: secure_crt.cpp:75
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define WARN_IF_PTR_LEN(len)
Definition: secure_crt.cpp:99
const Status INVALID_POINTER
Definition: status.h:420
#define tncpy_s
Definition: secure_crt.cpp:72
#define tchar
Definition: secure_crt.cpp:69
#define tvsprintf_s
Definition: secure_crt.cpp:79
const Status INVALID_SIZE
Definition: status.h:421
#define STATUS_ADD_DEFINITIONS(definitions)
add a module&#39;s array of StatusDefinition to the list.
Definition: status.h:216
static const StatusDefinition secureCrtStatusDefinitions[]
Definition: secure_crt.cpp:38
#define tsprintf_s
Definition: secure_crt.cpp:80
#define tvsnprintf
Definition: secure_crt.cpp:78