1 /* Copyright (c) 2010 Wildfire Games
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:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
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.
24 * partial implementation of VC8's secure CRT functions
27 #include "precompiled.h"
33 #include "lib/secure_crt.h"
36 # include <boost/algorithm/string/replace.hpp>
39 // we were included from wsecure_crt.cpp; skip all stuff that
40 // must only be done once.
42 static const StatusDefinition secureCrtStatusDefinitions
[] = {
43 { ERR::STRING_NOT_TERMINATED
, L
"Invalid string (no 0 terminator found in buffer)" }
45 STATUS_ADD_DEFINITIONS(secureCrtStatusDefinitions
);
49 // written against http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n1031.pdf .
50 // optimized for size - e.g. strcpy calls strncpy with n = SIZE_MAX.
52 // since char and wide versions of these functions are basically the same,
53 // this source file implements generic versions and bridges the differences
54 // with these macros. wsecure_crt.cpp #defines WSECURE_CRT and
55 // includes this file.
57 // Note: These defines are all #undef:ed at the end of the file - remember to
58 // add a corresponding #undef when adding a #define.
60 # define tchar wchar_t
61 # define tstring std::wstring
62 # define T(string_literal) L ## string_literal
63 # define tnlen wcsnlen
64 # define tncpy_s wcsncpy_s
65 # define tcpy_s wcscpy_s
66 # define tncat_s wcsncat_s
67 # define tcat_s wcscat_s
70 # define tvsnprintf vswprintf // used by implementation
71 # define tvsprintf_s vswprintf_s
72 # define tsprintf_s swprintf_s
75 # define tstring std::string
76 # define T(string_literal) string_literal
77 # define tnlen strnlen
78 # define tncpy_s strncpy_s
79 # define tcpy_s strcpy_s
80 # define tncat_s strncat_s
81 # define tcat_s strcat_s
84 # define tvsnprintf vsnprintf // used by implementation
85 # define tvsprintf_s vsprintf_s
86 # define tsprintf_s sprintf_s
87 #endif // #ifdef WSECURE_CRT
90 // return <retval> and raise an assertion if <condition> doesn't hold.
91 // usable as a statement.
92 #define ENFORCE(condition, err_to_warn, retval) STMT(\
95 DEBUG_WARN_ERR(err_to_warn); \
100 // raise a debug warning if <len> is the size of a pointer.
101 // catches bugs such as: tchar* s = ..; tcpy_s(s, sizeof(s), T(".."));
102 // if warnings get annoying, replace with debug_printf. usable as a statement.
104 // currently disabled due to high risk of false positives.
105 #define WARN_IF_PTR_LEN(len)\
107 ENSURE(len != sizeof(char*));
111 // skip our implementation if already available, but not the
112 // self-test and the t* defines (needed for test).
113 #if EMULATE_SECURE_CRT
115 #if !OS_UNIX || OS_MACOSX || OS_OPENBSD
116 // return length [in characters] of a string, not including the trailing
117 // null character. to protect against access violations, only the
118 // first <max_len> characters are examined; if the null character is
119 // not encountered by then, <max_len> is returned.
120 size_t tnlen(const tchar
* str
, size_t max_len
)
122 // note: we can't bail - what would the return value be?
125 WARN_IF_PTR_LEN(max_len
);
128 for(len
= 0; len
< max_len
; len
++)
137 static tstring
androidFormat(const tchar
*fmt
)
139 // FIXME handle %%hs, %%ls, etc
141 boost::algorithm::replace_all(ret
, T("%ls"), T("%S"));
142 boost::algorithm::replace_all(ret
, T("%hs"), T("%s"));
147 // copy at most <max_src_chars> (not including trailing null) from
148 // <src> into <dst>, which must not overlap.
149 // if thereby <max_dst_chars> (including null) would be exceeded,
150 // <dst> is set to the empty string and ERANGE returned; otherwise,
151 // 0 is returned to indicate success and that <dst> is null-terminated.
153 // note: padding with zeroes is not called for by N1031.
154 int tncpy_s(tchar
* dst
, size_t max_dst_chars
, const tchar
* src
, size_t max_src_chars
)
156 // the MS implementation returns EINVAL and allows dst = 0 if
157 // max_dst_chars = max_src_chars = 0. no mention of this in
158 // 3.6.2.1.1, so don't emulate that behavior.
159 ENFORCE(dst
!= 0, ERR::INVALID_POINTER
, EINVAL
);
160 ENFORCE(max_dst_chars
!= 0, ERR::INVALID_SIZE
, EINVAL
); // N1031 says ERANGE, MSDN/MSVC says EINVAL
161 *dst
= '\0'; // in case src ENFORCE is triggered
162 ENFORCE(src
!= 0, ERR::INVALID_POINTER
, EINVAL
);
164 WARN_IF_PTR_LEN(max_dst_chars
);
165 WARN_IF_PTR_LEN(max_src_chars
);
167 // copy string until null character encountered or limit reached.
168 // optimized for size (less comparisons than MS impl) and
169 // speed (due to well-predicted jumps; we don't bother unrolling).
171 size_t chars_left
= std::min(max_dst_chars
, max_src_chars
);
172 while(chars_left
!= 0)
174 // success: reached end of string normally.
175 if((*p
++ = *src
++) == '\0')
180 // which limit did we hit?
181 // .. dst, and last character wasn't null: overflow.
182 if(max_dst_chars
<= max_src_chars
)
185 ENFORCE(0, ERR::INVALID_SIZE
, ERANGE
);
187 // .. source: success, but still need to null-terminate the destination.
193 // copy <src> (including trailing null) into <dst>, which must not overlap.
194 // if thereby <max_dst_chars> (including null) would be exceeded,
195 // <dst> is set to the empty string and ERANGE returned; otherwise,
196 // 0 is returned to indicate success and that <dst> is null-terminated.
197 int tcpy_s(tchar
* dst
, size_t max_dst_chars
, const tchar
* src
)
199 return tncpy_s(dst
, max_dst_chars
, src
, SIZE_MAX
);
203 // append <src> to <dst>, which must not overlap.
204 // if thereby <max_dst_chars> (including null) would be exceeded,
205 // <dst> is set to the empty string and ERANGE returned; otherwise,
206 // 0 is returned to indicate success and that <dst> is null-terminated.
207 int tncat_s(tchar
* dst
, size_t max_dst_chars
, const tchar
* src
, size_t max_src_chars
)
209 ENFORCE(dst
!= 0, ERR::INVALID_POINTER
, EINVAL
);
210 ENFORCE(max_dst_chars
!= 0, ERR::INVALID_SIZE
, EINVAL
); // N1031 says ERANGE, MSDN/MSVC says EINVAL
211 // src is checked in tncpy_s
213 // WARN_IF_PTR_LEN not necessary: both max_dst_chars and max_src_chars
214 // are checked by tnlen / tncpy_s (respectively).
216 const size_t dst_len
= tnlen(dst
, max_dst_chars
);
217 if(dst_len
== max_dst_chars
)
220 ENFORCE(0, ERR::STRING_NOT_TERMINATED
, EINVAL
); // N1031/MSDN says ERANGE, MSVC says EINVAL
223 tchar
* const end
= dst
+dst_len
;
224 const size_t chars_left
= max_dst_chars
-dst_len
;
225 int ret
= tncpy_s(end
, chars_left
, src
, max_src_chars
);
226 // if tncpy_s overflowed, we need to clear the start of our string
227 // (not just the appended part). can't do that by default, because
228 // the beginning of dst is not changed in normal operation.
235 // append <src> to <dst>, which must not overlap.
236 // if thereby <max_dst_chars> (including null) would be exceeded,
237 // <dst> is set to the empty string and ERANGE returned; otherwise,
238 // 0 is returned to indicate success and that <dst> is null-terminated.
240 // note: implemented as tncat_s(dst, max_dst_chars, src, SIZE_MAX)
241 int tcat_s(tchar
* dst
, size_t max_dst_chars
, const tchar
* src
)
243 return tncat_s(dst
, max_dst_chars
, src
, SIZE_MAX
);
247 int tvsprintf_s(tchar
* dst
, size_t max_dst_chars
, const tchar
* fmt
, va_list ap
)
249 if(!dst
|| !fmt
|| max_dst_chars
== 0)
256 // Workaround for https://code.google.com/p/android/issues/detail?id=109074
257 // (vswprintf doesn't null-terminate strings)
258 memset(dst
, 0, max_dst_chars
* sizeof(tchar
));
260 const int ret
= tvsnprintf(dst
, max_dst_chars
, androidFormat(fmt
).c_str(), ap
);
262 const int ret
= tvsnprintf(dst
, max_dst_chars
, fmt
, ap
);
264 if(ret
< 0 || ret
>= int(max_dst_chars
)) // not enough space
269 return ret
; // negative if error, else # chars written (excluding '\0')
273 int tsprintf_s(tchar
* buf
, size_t max_chars
, const tchar
* fmt
, ...)
277 int len
= tvsprintf_s(buf
, max_chars
, fmt
, ap
);
282 #endif // #if EMULATE_SECURE_CRT