diff --git a/casadm/safeclib/LICENSE©ING.txt b/casadm/safeclib/LICENSE©ING.txt new file mode 100644 index 0000000..8e84778 --- /dev/null +++ b/casadm/safeclib/LICENSE©ING.txt @@ -0,0 +1,50 @@ +Safe C Library + + Copyright (c) 2014-2016, Intel Corporation. All rights reserved. + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ + +Copyright (C) 2012, 2013 Cisco Systems +All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/casadm/safeclib/ignore_handler_s.c b/casadm/safeclib/ignore_handler_s.c index 54b65c9..412e0cb 100644 --- a/casadm/safeclib/ignore_handler_s.c +++ b/casadm/safeclib/ignore_handler_s.c @@ -69,4 +69,4 @@ void ignore_handler_s(const char *msg, void *ptr, errno_t error) (msg) ? msg : "Null message"); return; } -EXPORT_SYMBOL(ignore_handler_s); +EXPORT_SYMBOL(ignore_handler_s) diff --git a/casadm/safeclib/memcpy_s.c b/casadm/safeclib/memcpy_s.c index a0d1f2e..a5c77b7 100644 --- a/casadm/safeclib/memcpy_s.c +++ b/casadm/safeclib/memcpy_s.c @@ -46,7 +46,9 @@ * * DESCRIPTION * This function copies at most smax bytes from src to dest, up to - * dmax. + * dmax. The size values are unsigned values. + * + * AR: Dave - verify ISO spec requires unsigned * * SPECIFIED IN * ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments @@ -72,8 +74,8 @@ * smax shall not be greater than dmax. * Copying shall not take place between regions that overlap. * If there is a runtime-constraint violation, the memcpy_s function - * stores zeros in the first dmax bytes of the region pointed to - * by dest if dest is not a null pointer and smax is valid. + * stores zeros in the first dmax bytes of the region pointed to + * by dest if dest is not a null pointer and smax is valid. * * RETURN VALUE * EOK successful operation @@ -114,6 +116,10 @@ memcpy_s (void *dest, rsize_t dmax, const void *src, rsize_t smax) return RCNEGATE(ESLEMAX); } + // AR: This is not a requirement according to the ISO spec - Change? + // AR: documentation needed on use of the error handlers - + // AR: default err handler should output to stderr on DEBUG + // AR: update docs to define return RCNEGATE of the error number if (smax == 0) { mem_prim_set(dp, dmax, 0); invoke_safe_mem_constraint_handler("memcpy_s: smax is 0", @@ -154,4 +160,4 @@ memcpy_s (void *dest, rsize_t dmax, const void *src, rsize_t smax) return RCNEGATE(EOK); } -EXPORT_SYMBOL(memcpy_s); +EXPORT_SYMBOL(memcpy_s) diff --git a/casadm/safeclib/memmove_s.c b/casadm/safeclib/memmove_s.c index d71cc88..d040d05 100644 --- a/casadm/safeclib/memmove_s.c +++ b/casadm/safeclib/memmove_s.c @@ -34,6 +34,7 @@ #include "mem_primitives_lib.h" #include "safe_mem_lib.h" + /** * NAME * memmove_s @@ -47,7 +48,7 @@ * DESCRIPTION * The memmove_s function copies smax bytes from the region pointed * to by src into the region pointed to by dest. This copying takes place - * as if the smax bytes from the region pointed to by src are first copied + * as if the smax bytes from the region pointed to by src are first copied * into a temporary array of smax bytes that does not overlap the region * pointed to by dest or src, and then the smax bytes from the temporary * array are copied into the object region to by dest. @@ -76,9 +77,9 @@ * dmax shall not be greater than RSIZE_MAX_MEM. * smax shall not be greater than dmax. * If there is a runtime-constraint violation, the memmove_s function - * stores zeros in the first dmax characters of the regionpointed to - * by dest if dest is not a null pointer and dmax is not greater - * than RSIZE_MAX_MEM. + * stores zeros in the first dmax characters of the regionpointed to + * by dest if dest is not a null pointer and dmax is not greater + * than RSIZE_MAX_MEM. * * RETURN VALUE * EOK successful operation @@ -145,4 +146,4 @@ memmove_s (void *dest, rsize_t dmax, const void *src, rsize_t smax) return (RCNEGATE(EOK)); } -EXPORT_SYMBOL(memmove_s); +EXPORT_SYMBOL(memmove_s) diff --git a/casadm/safeclib/memset_s.c b/casadm/safeclib/memset_s.c index 7782391..f19717d 100644 --- a/casadm/safeclib/memset_s.c +++ b/casadm/safeclib/memset_s.c @@ -102,4 +102,4 @@ memset_s (void *dest, rsize_t len, uint8_t value) return (RCNEGATE(EOK)); } -EXPORT_SYMBOL(memset_s); +EXPORT_SYMBOL(memset_s) diff --git a/casadm/safeclib/safe_lib.h b/casadm/safeclib/safe_lib.h index 8e02899..ba41b28 100644 --- a/casadm/safeclib/safe_lib.h +++ b/casadm/safeclib/safe_lib.h @@ -33,6 +33,10 @@ #ifndef __SAFE_LIB_H__ #define __SAFE_LIB_H__ +#ifdef __cplusplus +extern "C" { +#endif + #include "safe_types.h" #include "safe_lib_errno.h" @@ -58,4 +62,7 @@ extern void ignore_handler_s(const char *msg, void *ptr, errno_t error); #include "safe_mem_lib.h" #include "safe_str_lib.h" +#ifdef __cplusplus +} +#endif #endif /* __SAFE_LIB_H__ */ diff --git a/casadm/safeclib/safe_lib_errno.h b/casadm/safeclib/safe_lib_errno.h index a27e0f2..8f27111 100644 --- a/casadm/safeclib/safe_lib_errno.h +++ b/casadm/safeclib/safe_lib_errno.h @@ -83,6 +83,15 @@ #define ESNOTFND ( 409 ) /* not found */ #endif +/* Additional for safe snprintf_s interfaces */ +#ifndef ESBADFMT +#define ESBADFMT ( 410 ) /* bad format string */ +#endif + +#ifndef ESFMTTYP +#define ESFMTTYP ( 411 ) /* bad format type */ +#endif + /* EOK may or may not be defined in errno.h */ #ifndef EOK #define EOK ( 0 ) diff --git a/casadm/safeclib/safe_mem_constraint.c b/casadm/safeclib/safe_mem_constraint.c index 00e82a2..9fafe9e 100644 --- a/casadm/safeclib/safe_mem_constraint.c +++ b/casadm/safeclib/safe_mem_constraint.c @@ -98,7 +98,7 @@ set_mem_constraint_handler_s (constraint_handler_t handler) } return prev_handler; } -EXPORT_SYMBOL(set_mem_constraint_handler_s); +EXPORT_SYMBOL(set_mem_constraint_handler_s) /** diff --git a/casadm/safeclib/safe_mem_lib.h b/casadm/safeclib/safe_mem_lib.h index 89aacbc..162efd2 100644 --- a/casadm/safeclib/safe_mem_lib.h +++ b/casadm/safeclib/safe_mem_lib.h @@ -34,6 +34,7 @@ #define __SAFE_MEM_LIB_H__ #include "safe_lib.h" +#include #define RSIZE_MAX_MEM ( 256UL << 20 ) /* 256MB */ #define RSIZE_MAX_MEM16 ( RSIZE_MAX_MEM/2 ) @@ -43,15 +44,79 @@ extern constraint_handler_t set_mem_constraint_handler_s(constraint_handler_t handler); + +/* compare memory */ +extern errno_t memcmp_s(const void *dest, rsize_t dmax, + const void *src, rsize_t slen, int *diff); + +/* compare uint16_t memory */ +extern errno_t memcmp16_s(const uint16_t *dest, rsize_t dmax, + const uint16_t *src, rsize_t slen, int *diff); + +/* compare uint32_t memory */ +extern errno_t memcmp32_s(const uint32_t *dest, rsize_t dmax, + const uint32_t *src, rsize_t slen, int *diff); + +/* wide compare memory */ +extern errno_t wmemcmp_s(const wchar_t *dest, rsize_t dmax, + const wchar_t *src, rsize_t smax, int *diff); + + /* copy memory */ extern errno_t memcpy_s(void *dest, rsize_t dmax, const void *src, rsize_t slen); -/* set bytes */ -extern errno_t memset_s(void *dest, rsize_t dmax, uint8_t value); +/* copy uint16_t memory */ +extern errno_t memcpy16_s(uint16_t *dest, rsize_t dmax, + const uint16_t *src, rsize_t slen); + +/* copy uint32_t memory */ +extern errno_t memcpy32_s(uint32_t *dest, rsize_t dmax, + const uint32_t *src, rsize_t slen); + +/* copy wchar_t memory */ +extern errno_t wmemcpy_s(wchar_t *dest, rsize_t dmax, + const wchar_t *src, rsize_t slen); + /* move memory, including overlapping memory */ extern errno_t memmove_s(void *dest, rsize_t dmax, const void *src, rsize_t slen); +/* uint16_t move memory, including overlapping memory */ +extern errno_t memmove16_s(uint16_t *dest, rsize_t dmax, + const uint16_t *src, rsize_t slen); + +/* uint32_t move memory, including overlapping memory */ +extern errno_t memmove32_s(uint32_t *dest, rsize_t dmax, + const uint32_t *src, rsize_t slen); + +/* copy wchar_t memory, including overlapping memory */ +extern errno_t wmemmove_s(wchar_t *dest, rsize_t dmax, + const wchar_t *src, rsize_t slen); + + +/* set bytes */ +extern errno_t memset_s(void *dest, rsize_t dmax, uint8_t value); + +/* set uint16_t */ +extern errno_t memset16_s(uint16_t *dest, rsize_t dmax, uint16_t value); + +/* set uint32_t */ +extern errno_t memset32_s(uint32_t *dest, rsize_t dmax, uint32_t value); + +/* wide set bytes */ +extern errno_t wmemset_s(wchar_t *dest, wchar_t value, rsize_t len); + + +/* byte zero */ +extern errno_t memzero_s(void *dest, rsize_t dmax); + +/* uint16_t zero */ +extern errno_t memzero16_s(uint16_t *dest, rsize_t dmax); + +/* uint32_t zero */ +extern errno_t memzero32_s(uint32_t *dest, rsize_t dmax); + + #endif /* __SAFE_MEM_LIB_H__ */ diff --git a/casadm/safeclib/safe_str_constraint.c b/casadm/safeclib/safe_str_constraint.c index 17e7fbb..d57058f 100644 --- a/casadm/safeclib/safe_str_constraint.c +++ b/casadm/safeclib/safe_str_constraint.c @@ -102,7 +102,7 @@ set_str_constraint_handler_s (constraint_handler_t handler) } return prev_handler; } -EXPORT_SYMBOL(set_str_constraint_handler_s); +EXPORT_SYMBOL(set_str_constraint_handler_s) /** diff --git a/casadm/safeclib/safe_str_constraint.h b/casadm/safeclib/safe_str_constraint.h index d0de95d..a1fba3e 100644 --- a/casadm/safeclib/safe_str_constraint.h +++ b/casadm/safeclib/safe_str_constraint.h @@ -61,4 +61,18 @@ static inline void handle_error(char *orig_dest, rsize_t orig_dmax, return; } +static inline void handle_wc_error(wchar_t *orig_dest, rsize_t orig_dmax, + char *err_msg, errno_t err_code) +{ +#ifdef SAFECLIB_STR_NULL_SLACK + /* null string to eliminate partial copy */ + while (orig_dmax) { *orig_dest = L'\0'; orig_dmax--; orig_dest++; } +#else + *orig_dest = L'\0'; +#endif + + invoke_safe_str_constraint_handler(err_msg, NULL, err_code); + return; +} + #endif /* __SAFE_STR_CONSTRAINT_H__ */ diff --git a/casadm/safeclib/safe_str_lib.h b/casadm/safeclib/safe_str_lib.h index 0d5c8ef..e50df36 100644 --- a/casadm/safeclib/safe_str_lib.h +++ b/casadm/safeclib/safe_str_lib.h @@ -33,6 +33,7 @@ #define __SAFE_STR_LIB_H__ #include "safe_lib.h" +#include /* * The shortest string is a null string!! @@ -52,20 +53,241 @@ #define SAFE_STR_PASSWORD_MIN_LENGTH ( 6 ) #define SAFE_STR_PASSWORD_MAX_LENGTH ( 32 ) + /* set string constraint handler */ extern constraint_handler_t set_str_constraint_handler_s(constraint_handler_t handler); + +/* string compare */ +extern errno_t +strcasecmp_s(const char *dest, rsize_t dmax, + const char *src, int *indicator); + + +/* find a substring _ case insensitive */ +extern errno_t +strcasestr_s(char *dest, rsize_t dmax, + const char *src, rsize_t slen, char **substring); + + +/* string concatenate */ +extern errno_t +strcat_s(char *dest, rsize_t dmax, const char *src); + + +/* string compare */ +extern errno_t +strcmp_s(const char *dest, rsize_t dmax, + const char *src, int *indicator); + + +/* fixed field string compare */ +extern errno_t +strcmpfld_s(const char *dest, rsize_t dmax, + const char *src, int *indicator); + + +/* string copy */ +extern errno_t +strcpy_s(char *dest, rsize_t dmax, const char *src); + +/* string copy */ +extern char * +stpcpy_s(char *dest, rsize_t dmax, const char *src, errno_t *err); + +/* string copy */ +extern char * +stpncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t smax, errno_t *err); + +/* fixed char array copy */ +extern errno_t +strcpyfld_s(char *dest, rsize_t dmax, const char *src, rsize_t slen); + + +/* copy from a null terminated string to fixed char array */ +extern errno_t +strcpyfldin_s(char *dest, rsize_t dmax, const char *src, rsize_t slen); + + +/* copy from a char array to null terminated string */ +extern errno_t +strcpyfldout_s(char *dest, rsize_t dmax, const char *src, rsize_t slen); + + +/* computes excluded prefix length */ +extern errno_t +strcspn_s(const char *dest, rsize_t dmax, + const char *src, rsize_t slen, rsize_t *count); + + +/* returns a pointer to the first occurrence of c in dest */ +extern errno_t +strfirstchar_s(char *dest, rsize_t dmax, char c, char **first); + + +/* returns index of first difference */ +extern errno_t +strfirstdiff_s(const char *dest, rsize_t dmax, + const char *src, rsize_t *index); + + +/* validate alphanumeric string */ +extern bool +strisalphanumeric_s(const char *str, rsize_t slen); + + +/* validate ascii string */ +extern bool +strisascii_s(const char *str, rsize_t slen); + + +/* validate string of digits */ +extern bool +strisdigit_s(const char *str, rsize_t slen); + + +/* validate hex string */ +extern bool +strishex_s(const char *str, rsize_t slen); + + +/* validate lower case */ +extern bool +strislowercase_s(const char *str, rsize_t slen); + + +/* validate mixed case */ +extern bool +strismixedcase_s(const char *str, rsize_t slen); + + +/* validate password */ +extern bool +strispassword_s(const char *str, rsize_t slen); + + +/* validate upper case */ +extern bool +strisuppercase_s(const char *str, rsize_t slen); + + +/* returns a pointer to the last occurrence of c in s1 */ +extern errno_t +strlastchar_s(char *str, rsize_t smax, char c, char **first); + + +/* returns index of last difference */ +extern errno_t +strlastdiff_s(const char *dest, rsize_t dmax, + const char *src, rsize_t *index); + + +/* left justify */ +extern errno_t +strljustify_s(char *dest, rsize_t dmax); + + +/* fitted string concatenate */ +extern errno_t +strncat_s(char *dest, rsize_t dmax, const char *src, rsize_t slen); + + /* fitted string copy */ extern errno_t strncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t slen); + /* string length */ extern rsize_t strnlen_s (const char *s, rsize_t smax); + +/* string terminate */ +extern rsize_t +strnterminate_s (char *s, rsize_t smax); + + +/* get pointer to first occurrence from set of char */ +extern errno_t +strpbrk_s(char *dest, rsize_t dmax, + char *src, rsize_t slen, char **first); + + +extern errno_t +strfirstsame_s(const char *dest, rsize_t dmax, + const char *src, rsize_t *index); + +extern errno_t +strlastsame_s(const char *dest, rsize_t dmax, + const char *src, rsize_t *index); + + +/* searches for a prefix */ +extern errno_t +strprefix_s(const char *dest, rsize_t dmax, const char *src); + + +/* removes leading and trailing white space */ +extern errno_t +strremovews_s(char *dest, rsize_t dmax); + + +/* computes inclusive prefix length */ +extern errno_t +strspn_s(const char *dest, rsize_t dmax, + const char *src, rsize_t slen, rsize_t *count); + + +/* find a substring */ +extern errno_t +strstr_s(char *dest, rsize_t dmax, + const char *src, rsize_t slen, char **substring); + + /* string tokenizer */ extern char * strtok_s(char *s1, rsize_t *s1max, const char *src, char **ptr); + +/* convert string to lowercase */ +extern errno_t +strtolowercase_s(char *str, rsize_t slen); + + +/* convert string to uppercase */ +extern errno_t +strtouppercase_s(char *str, rsize_t slen); + + +/* zero an entire string with nulls */ +extern errno_t +strzero_s(char *dest, rsize_t dmax); + + +/* wide string copy */ +extern wchar_t * +wcpcpy_s(wchar_t* dest, rsize_t dmax, const wchar_t* src, errno_t *err); + +/* wide string concatenate */ +extern errno_t +wcscat_s(wchar_t* dest, rsize_t dmax, const wchar_t* src); + +/* fitted wide string concatenate */ +extern errno_t +wcsncat_s(wchar_t *dest, rsize_t dmax, const wchar_t *src, rsize_t slen); + +/* wide string copy */ +errno_t +wcscpy_s(wchar_t* dest, rsize_t dmax, const wchar_t* src); + +/* fitted wide string copy */ +extern errno_t +wcsncpy_s(wchar_t* dest, rsize_t dmax, const wchar_t* src, rsize_t slen); + +/* wide string length */ +extern rsize_t +wcsnlen_s(const wchar_t *dest, rsize_t dmax); + + #endif /* __SAFE_STR_LIB_H__ */ diff --git a/casadm/safeclib/safe_types.h b/casadm/safeclib/safe_types.h index 057063f..dba7474 100644 --- a/casadm/safeclib/safe_types.h +++ b/casadm/safeclib/safe_types.h @@ -46,7 +46,16 @@ typedef int errno_t; #else #include + +/* For systems without sys/types.h, prefer to get size_t from stdlib.h */ +/* some armcc environments don't have a sys/types.h in the environment */ +#ifdef _USE_STDLIB +#include +#include +#else #include +#endif + #include #include #include diff --git a/casadm/safeclib/strncpy_s.c b/casadm/safeclib/strncpy_s.c index e1cdd6b..1fcdf6f 100644 --- a/casadm/safeclib/strncpy_s.c +++ b/casadm/safeclib/strncpy_s.c @@ -235,4 +235,4 @@ strncpy_s (char *dest, rsize_t dmax, const char *src, rsize_t slen) ESNOSPC); return RCNEGATE(ESNOSPC); } -EXPORT_SYMBOL(strncpy_s); +EXPORT_SYMBOL(strncpy_s) diff --git a/casadm/safeclib/strnlen_s.c b/casadm/safeclib/strnlen_s.c index 3df9d26..6597551 100644 --- a/casadm/safeclib/strnlen_s.c +++ b/casadm/safeclib/strnlen_s.c @@ -55,7 +55,7 @@ * INPUT PARAMETERS * dest pointer to string * - * dmax restricted maximum length (including null character). + * dmax restricted maximum length. * * OUTPUT PARAMETERS * none @@ -64,15 +64,16 @@ * dest shall not be a null pointer * dmax shall not be greater than RSIZE_MAX_STR * dmax shall not equal zero - * null character shall be in first dmax characters of dest * * RETURN VALUE * The function returns the string length, excluding the terminating * null character. If dest is NULL, then strnlen_s returns 0. * * Otherwise, the strnlen_s function returns the number of characters - * that precede the terminating null character. - * At most the first dmax characters of dest are accessed by strnlen_s. + * that precede the terminating null character. If there is no null + * character in the first dmax characters of dest then strnlen_s returns + * dmax. At most the first dmax characters of dest are accessed + * by strnlen_s. * * ALSO SEE * strnterminate_s() @@ -82,7 +83,6 @@ rsize_t strnlen_s (const char *dest, rsize_t dmax) { rsize_t count; - rsize_t orig_dmax = dmax; if (dest == NULL) { return RCNEGATE(0); @@ -106,12 +106,7 @@ strnlen_s (const char *dest, rsize_t dmax) dmax--; dest++; } - if (count == orig_dmax) { - invoke_safe_str_constraint_handler("strnlen_s: string length exceeds dmax", - NULL, ESLEMAX); - return RCNEGATE(0); - } return RCNEGATE(count); } -EXPORT_SYMBOL(strnlen_s); +EXPORT_SYMBOL(strnlen_s) diff --git a/casadm/safeclib/strtok_s.c b/casadm/safeclib/strtok_s.c index 97b89fe..2b5f29a 100644 --- a/casadm/safeclib/strtok_s.c +++ b/casadm/safeclib/strtok_s.c @@ -55,7 +55,7 @@ * dmax points to an object whose value is the number of elements * in the character array pointed to by the first argument. The * first call stores an initial value in the object pointed to by - * ptr and updates the value pointed to by dmax to reflect the + * ptr and updates the value pointed to by dmax to reflect the * number of elements that remain in relation to ptr. Subsequent * calls in the sequence have a null first argument and the objects * pointed to by dmax and ptr are required to have the values @@ -79,9 +79,9 @@ * overwritten by a null character, which terminates the * current token. * - * In all cases, the strtok_s function stores sufficient information + * In all cases, the strtok_s function stores sufficient information * in the pointer pointed to by ptr so that subsequent calls, - * with a null pointer for dest and the unmodified pointer value + * with a null pointer for dest and the unmodified pointer value * for ptr, shall start searching just past the element overwritten * by a null character (if any). * @@ -321,3 +321,5 @@ strtok_s(char *dest, rsize_t *dmax, const char *src, char **ptr) *dmax = dlen; return (ptoken); } +EXPORT_SYMBOL(strtok_s) +