LCOV - code coverage report
Current view: top level - source/lib - secure_crt.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 49 49 100.0 %
Date: 2023-01-19 00:18:29 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /* Copyright (C) 2020 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 <cstdio>
      30             : #include <cstring>
      31             : #include <cerrno>
      32             : #include <cstdarg>
      33             : 
      34             : #include "lib/secure_crt.h"
      35             : 
      36             : #if OS_ANDROID
      37             : # include <boost/algorithm/string/replace.hpp>
      38             : #endif
      39             : 
      40             : // we were included from wsecure_crt.cpp; skip all stuff that
      41             : // must only be done once.
      42             : #ifndef WSECURE_CRT
      43             : static const StatusDefinition secureCrtStatusDefinitions[] = {
      44             :     { ERR::STRING_NOT_TERMINATED, L"Invalid string (no 0 terminator found in buffer)" }
      45             : };
      46           1 : STATUS_ADD_DEFINITIONS(secureCrtStatusDefinitions);
      47             : #endif
      48             : 
      49             : 
      50             : // written against http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n1031.pdf .
      51             : // optimized for size - e.g. strcpy calls strncpy with n = SIZE_MAX.
      52             : 
      53             : // since char and wide versions of these functions are basically the same,
      54             : // this source file implements generic versions and bridges the differences
      55             : // with these macros. wsecure_crt.cpp #defines WSECURE_CRT and
      56             : // includes this file.
      57             : 
      58             : // Note: These defines are all #undef:ed at the end of the file - remember to
      59             : // add a corresponding #undef when adding a #define.
      60             : #ifdef WSECURE_CRT
      61             : # define tchar wchar_t
      62             : # define tstring std::wstring
      63             : # define T(string_literal) L ## string_literal
      64             : # define tnlen wcsnlen
      65             : # define tncpy_s wcsncpy_s
      66             : # define tcpy_s wcscpy_s
      67             : # define tncat_s wcsncat_s
      68             : # define tcat_s wcscat_s
      69             : # define tcmp wcscmp
      70             : # define tcpy wcscpy
      71             : # define tvsnprintf vswprintf   // used by implementation
      72             : # define tvsprintf_s vswprintf_s
      73             : # define tsprintf_s swprintf_s
      74             : #else
      75             : # define tchar char
      76             : # define tstring std::string
      77             : # define T(string_literal) string_literal
      78             : # define tnlen strnlen
      79             : # define tncpy_s strncpy_s
      80             : # define tcpy_s strcpy_s
      81             : # define tncat_s strncat_s
      82             : # define tcat_s strcat_s
      83             : # define tcmp strcmp
      84             : # define tcpy strcpy
      85             : # define tvsnprintf vsnprintf   // used by implementation
      86             : # define tvsprintf_s vsprintf_s
      87             : # define tsprintf_s sprintf_s
      88             : #endif  // #ifdef WSECURE_CRT
      89             : 
      90             : 
      91             : // return <retval> and raise an assertion if <condition> doesn't hold.
      92             : // usable as a statement.
      93             : #define ENFORCE(condition, err_to_warn, retval) STMT(\
      94             :     if(!(condition))                    \
      95             :     {                                   \
      96             :         DEBUG_WARN_ERR(err_to_warn);    \
      97             :         return retval;                  \
      98             :     }                                   \
      99             : )
     100             : 
     101             : // raise a debug warning if <len> is the size of a pointer.
     102             : // catches bugs such as: tchar* s = ..; tcpy_s(s, sizeof(s), T(".."));
     103             : // if warnings get annoying, replace with debug_printf. usable as a statement.
     104             : //
     105             : // currently disabled due to high risk of false positives.
     106             : #define WARN_IF_PTR_LEN(len)\
     107             : /*
     108             :     ENSURE(len != sizeof(char*));
     109             : */
     110             : 
     111             : 
     112             : // skip our implementation if already available, but not the
     113             : // self-test and the t* defines (needed for test).
     114             : #if EMULATE_SECURE_CRT
     115             : 
     116             : #if !OS_UNIX || OS_MACOSX || OS_OPENBSD
     117             : // return length [in characters] of a string, not including the trailing
     118             : // null character. to protect against access violations, only the
     119             : // first <max_len> characters are examined; if the null character is
     120             : // not encountered by then, <max_len> is returned.
     121             : size_t tnlen(const tchar* str, size_t max_len)
     122             : {
     123             :     // note: we can't bail - what would the return value be?
     124             :     ENSURE(str != 0);
     125             : 
     126             :     WARN_IF_PTR_LEN(max_len);
     127             : 
     128             :     size_t len;
     129             :     for(len = 0; len < max_len; len++)
     130             :         if(*str++ == '\0')
     131             :             break;
     132             : 
     133             :     return len;
     134             : }
     135             : #endif // !OS_UNIX
     136             : 
     137             : #if OS_ANDROID
     138             : static tstring androidFormat(const tchar *fmt)
     139             : {
     140             :     // FIXME handle %%hs, %%ls, etc
     141             :     tstring ret(fmt);
     142             :     boost::algorithm::replace_all(ret, T("%ls"), T("%S"));
     143             :     boost::algorithm::replace_all(ret, T("%hs"), T("%s"));
     144             :     return ret;
     145             : }
     146             : #endif
     147             : 
     148             : // copy at most <max_src_chars> (not including trailing null) from
     149             : // <src> into <dst>, which must not overlap.
     150             : // if thereby <max_dst_chars> (including null) would be exceeded,
     151             : // <dst> is set to the empty string and ERANGE returned; otherwise,
     152             : // 0 is returned to indicate success and that <dst> is null-terminated.
     153             : //
     154             : // note: padding with zeroes is not called for by N1031.
     155        5137 : int tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)
     156             : {
     157             :     // the MS implementation returns EINVAL and allows dst = 0 if
     158             :     // max_dst_chars = max_src_chars = 0. no mention of this in
     159             :     // 3.6.2.1.1, so don't emulate that behavior.
     160        5137 :     ENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);
     161        5134 :     ENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL); // N1031 says ERANGE, MSDN/MSVC says EINVAL
     162        5133 :     *dst = '\0';    // in case src ENFORCE is triggered
     163        5133 :     ENFORCE(src != 0, ERR::INVALID_POINTER, EINVAL);
     164             : 
     165             :     WARN_IF_PTR_LEN(max_dst_chars);
     166             :     WARN_IF_PTR_LEN(max_src_chars);
     167             : 
     168             :     // copy string until null character encountered or limit reached.
     169             :     // optimized for size (less comparisons than MS impl) and
     170             :     // speed (due to well-predicted jumps; we don't bother unrolling).
     171        5131 :     tchar* p = dst;
     172        5131 :     size_t chars_left = std::min(max_dst_chars, max_src_chars);
     173      103055 :     while(chars_left != 0)
     174             :     {
     175             :         // success: reached end of string normally.
     176       53634 :         if((*p++ = *src++) == '\0')
     177        4672 :             return 0;
     178       48962 :         chars_left--;
     179             :     }
     180             : 
     181             :     // which limit did we hit?
     182             :     // .. dst, and last character wasn't null: overflow.
     183         459 :     if(max_dst_chars <= max_src_chars)
     184             :     {
     185          16 :         *dst = '\0';
     186          16 :         ENFORCE(0, ERR::INVALID_SIZE, ERANGE);
     187             :     }
     188             :     // .. source: success, but still need to null-terminate the destination.
     189         443 :     *p = '\0';
     190         443 :     return 0;
     191             : }
     192             : 
     193             : 
     194             : // copy <src> (including trailing null) into <dst>, which must not overlap.
     195             : // if thereby <max_dst_chars> (including null) would be exceeded,
     196             : // <dst> is set to the empty string and ERANGE returned; otherwise,
     197             : // 0 is returned to indicate success and that <dst> is null-terminated.
     198        4673 : int tcpy_s(tchar* dst, size_t max_dst_chars, const tchar* src)
     199             : {
     200        4673 :     return tncpy_s(dst, max_dst_chars, src, SIZE_MAX);
     201             : }
     202             : 
     203             : 
     204             : // append <src> to <dst>, which must not overlap.
     205             : // if thereby <max_dst_chars> (including null) would be exceeded,
     206             : // <dst> is set to the empty string and ERANGE returned; otherwise,
     207             : // 0 is returned to indicate success and that <dst> is null-terminated.
     208          31 : int tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)
     209             : {
     210          31 :     ENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);
     211          28 :     ENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL); // N1031 says ERANGE, MSDN/MSVC says EINVAL
     212             :     // src is checked in tncpy_s
     213             : 
     214             :     // WARN_IF_PTR_LEN not necessary: both max_dst_chars and max_src_chars
     215             :     // are checked by tnlen / tncpy_s (respectively).
     216             : 
     217          27 :     const size_t dst_len = tnlen(dst, max_dst_chars);
     218          27 :     if(dst_len == max_dst_chars)
     219             :     {
     220           1 :         *dst = '\0';
     221           1 :         ENFORCE(0, ERR::STRING_NOT_TERMINATED, EINVAL); // N1031/MSDN says ERANGE, MSVC says EINVAL
     222             :     }
     223             : 
     224          26 :     tchar* const end = dst+dst_len;
     225          26 :     const size_t chars_left = max_dst_chars-dst_len;
     226          26 :     int ret = tncpy_s(end, chars_left, src, max_src_chars);
     227             :     // if tncpy_s overflowed, we need to clear the start of our string
     228             :     // (not just the appended part). can't do that by default, because
     229             :     // the beginning of dst is not changed in normal operation.
     230          26 :     if(ret != 0)
     231          11 :         *dst = '\0';
     232          26 :     return ret;
     233             : }
     234             : 
     235             : 
     236             : // append <src> to <dst>, which must not overlap.
     237             : // if thereby <max_dst_chars> (including null) would be exceeded,
     238             : // <dst> is set to the empty string and ERANGE returned; otherwise,
     239             : // 0 is returned to indicate success and that <dst> is null-terminated.
     240             : //
     241             : // note: implemented as tncat_s(dst, max_dst_chars, src, SIZE_MAX)
     242          16 : int tcat_s(tchar* dst, size_t max_dst_chars, const tchar* src)
     243             : {
     244          16 :     return tncat_s(dst, max_dst_chars, src, SIZE_MAX);
     245             : }
     246             : 
     247             : 
     248         441 : int tvsprintf_s(tchar* dst, size_t max_dst_chars, const tchar* fmt, va_list ap)
     249             : {
     250         441 :     if(!dst || !fmt || max_dst_chars == 0)
     251             :     {
     252           6 :         errno = EINVAL;
     253           6 :         return -1;
     254             :     }
     255             : 
     256             : #if OS_ANDROID
     257             :     // Workaround for https://code.google.com/p/android/issues/detail?id=109074
     258             :     // (vswprintf doesn't null-terminate strings)
     259             :     memset(dst, 0, max_dst_chars * sizeof(tchar));
     260             : 
     261             :     const int ret = tvsnprintf(dst, max_dst_chars, androidFormat(fmt).c_str(), ap);
     262             : #else
     263         435 :     const int ret = tvsnprintf(dst, max_dst_chars, fmt, ap);
     264             : #endif
     265         435 :     if(ret < 0 || ret >= int(max_dst_chars))  // not enough space
     266             :     {
     267           4 :         dst[0] = '\0';
     268           4 :         return -1;
     269             :     }
     270         431 :     return ret; // negative if error, else # chars written (excluding '\0')
     271             : }
     272             : 
     273             : 
     274         399 : int tsprintf_s(tchar* buf, size_t max_chars, const tchar* fmt, ...)
     275             : {
     276             :     va_list ap;
     277         399 :     va_start(ap, fmt);
     278         399 :     int len = tvsprintf_s(buf, max_chars, fmt, ap);
     279         399 :     va_end(ap);
     280         399 :     return len;
     281           6 : }
     282             : 
     283             : #endif // #if EMULATE_SECURE_CRT
     284             : 
     285             : #undef tchar
     286             : #undef T
     287             : #undef tnlen
     288             : #undef tncpy_s
     289             : #undef tcpy_s
     290             : #undef tncat_s
     291             : #undef tcat_s
     292             : #undef tcmp
     293             : #undef tcpy
     294             : #undef tvsnprintf
     295             : #undef tvsprintf_s
     296             : #undef tsprintf_s

Generated by: LCOV version 1.13