1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "securecutil.h"
17
18 #if SECUREC_HAVE_STRNLEN
19 #define SECUREC_STRCAT_LEN_THRESHOLD 8
20
21 #define SECUREC_CALC_STR_LEN(str,maxLen,len) do { \
22 if (*((str) + 0) == '\0') { \
23 len = 0; \
24 } else if (*((str) + 1) == '\0') { \
25 len = 1; \
26 } else if (*((str) + 2) == '\0') { \
27 len = 2; \
28 } else if (*((str) + 3) == '\0') { \
29 len = 3; \
30 } else if (*((str) + 4) == '\0') { \
31 len = 4; \
32 } else if (*((str) + 5) == '\0') { \
33 len = 5; \
34 } else if (*((str) + 6) == '\0') { \
35 len = 6; \
36 } else if (*((str) + 7) == '\0') { \
37 len = 7; \
38 } else if (*((str) + 8) == '\0') { \
39 /* Optimization with a length of 8 */ \
40 len = 8; \
41 } else { \
42 /* The offset is 8 because the performance of 8 byte alignment is high */ \
43 len = SECUREC_STRCAT_LEN_THRESHOLD + \
44 strnlen((str) + SECUREC_STRCAT_LEN_THRESHOLD, \
45 (maxLen) - SECUREC_STRCAT_LEN_THRESHOLD); \
46 } \
47 } SECUREC_WHILE_ZERO
48
49 /* The function compiler will be inlined and not placed in other files */
SecMinStrLenOpt(const char * str,size_t maxLen)50 static size_t SecMinStrLenOpt(const char *str, size_t maxLen)
51 {
52 size_t len;
53 if (maxLen > SECUREC_STRCAT_LEN_THRESHOLD) {
54 /* Just to reduce the code complexity */
55 SECUREC_CALC_STR_LEN(str, maxLen, len);
56 } else {
57 const char *strEnd = str;
58 len = 0;
59 /* use count as boundary checker */
60 while (len < maxLen && *(strEnd) != '\0') {
61 ++strEnd;
62 ++len; /* no ending terminator */
63 }
64 }
65 return len;
66 }
67
SecDoStrcat(char * strDest,size_t destMax,const char * strSrc)68 static errno_t SecDoStrcat(char *strDest, size_t destMax, const char *strSrc)
69 {
70 size_t destLen = strnlen(strDest, destMax);
71 /* Only optimize strSrc, do not apply this function to strDest */
72 size_t srcLen = SecMinStrLenOpt(strSrc, destMax - destLen);
73
74 if ((strDest < strSrc && strDest + destLen + srcLen >= strSrc) || \
75 (strSrc < strDest && strSrc + srcLen >= strDest)) {
76 strDest[0] = '\0';
77 if (strDest + destLen <= strSrc && destLen == destMax) {
78 SECUREC_ERROR_INVALID_PARAMTER("strcat_s");
79 return EINVAL_AND_RESET;
80 }
81 SECUREC_ERROR_BUFFER_OVERLAP("strcat_s");
82 return EOVERLAP_AND_RESET;
83 }
84 if (srcLen + destLen >= destMax || strDest == strSrc) {
85 strDest[0] = '\0';
86 if (destLen == destMax) {
87 SECUREC_ERROR_INVALID_PARAMTER("strcat_s");
88 return EINVAL_AND_RESET;
89 }
90 SECUREC_ERROR_INVALID_RANGE("strcat_s");
91 return ERANGE_AND_RESET;
92 }
93 (void)memcpy(strDest + destLen, strSrc, srcLen + 1); /* copy terminator */
94 return EOK;
95 }
96 #else
SecDoStrcat(char * strDest,size_t destMax,const char * strSrc)97 static errno_t SecDoStrcat(char *strDest, size_t destMax, const char *strSrc)
98 {
99 char *tmpDest = strDest;
100 const char *tmpSrc = strSrc;
101 size_t availableSize = destMax;
102 SECUREC_IN_REGISTER const char *overlapGuard = NULL;
103
104 if (tmpDest < tmpSrc) {
105 overlapGuard = tmpSrc;
106 while (availableSize > 0 && *tmpDest != 0) {
107 if (tmpDest == overlapGuard) {
108 strDest[0] = '\0';
109 SECUREC_ERROR_BUFFER_OVERLAP("strcat_s");
110 return EOVERLAP_AND_RESET;
111 }
112 /* seek to string end */
113 ++tmpDest;
114 --availableSize;
115 }
116
117 /* strDest unterminated, return error. */
118 if (availableSize == 0) {
119 strDest[0] = '\0';
120 SECUREC_ERROR_INVALID_PARAMTER("strcat_s");
121 return EINVAL_AND_RESET;
122 }
123 /* if availableSize > 0, then execute the strcat operation */
124 while ((*tmpDest++ = *tmpSrc++) != 0 && --availableSize > 0) {
125 if (tmpDest == overlapGuard) {
126 strDest[0] = '\0';
127 SECUREC_ERROR_BUFFER_OVERLAP("strcat_s");
128 return EOVERLAP_AND_RESET;
129 }
130 }
131 } else {
132 overlapGuard = tmpDest;
133 while (availableSize > 0 && *tmpDest != '\0') {
134 /* seek to string end, and no need to check overlap */
135 ++tmpDest;
136 --availableSize;
137 }
138
139 /* strDest unterminated, return error. */
140 if (availableSize == 0) {
141 strDest[0] = '\0';
142 SECUREC_ERROR_INVALID_PARAMTER("strcat_s");
143 return EINVAL_AND_RESET;
144 }
145 while ((*tmpDest++ = *tmpSrc++) != '\0' && --availableSize > 0) {
146 if (tmpSrc == overlapGuard) {
147 strDest[0] = '\0';
148 SECUREC_ERROR_BUFFER_OVERLAP("strcat_s");
149 return EOVERLAP_AND_RESET;
150 }
151 }
152 }
153
154 /* strDest have not enough space, return error */
155 if (availableSize == 0) {
156 strDest[0] = '\0';
157 SECUREC_ERROR_INVALID_RANGE("strcat_s");
158 return ERANGE_AND_RESET;
159 }
160 return EOK;
161 }
162 #endif
163 /*******************************************************************************
164 * <FUNCTION DESCRIPTION>
165 * The strcat_s function appends a copy of the string pointed to by strSrc (including the terminating null character)
166 * to the end of the string pointed to by strDest.
167 * The initial character of strSrc overwrites the terminating null character of strDest.
168 * strcat_s will return EOVERLAP_AND_RESET if the source and destination strings overlap.
169 *
170 * Note that the second parameter is the total size of the buffer, not the
171 * remaining size.
172 *
173 * <INPUT PARAMETERS>
174 * strDest Null-terminated destination string buffer.
175 * destMax Size of the destination string buffer.
176 * strSrc Null-terminated source string buffer.
177 *
178 * <OUTPUT PARAMETERS>
179 * strDest is updated
180 *
181 * <RETURN VALUE>
182 * EOK Success
183 * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN
184 * EINVAL_AND_RESET (strDest unterminated and all other parameters are valid)or
185 * (strDest != NULL and strSrc is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN)
186 * ERANGE destMax is 0 and destMax > SECUREC_STRING_MAX_LEN
187 * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap
188 * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid
189 *
190 * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid
191 *******************************************************************************
192 */
193
strcat_s(char * strDest,size_t destMax,const char * strSrc)194 errno_t strcat_s(char *strDest, size_t destMax, const char *strSrc)
195 {
196
197 if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN) {
198 SECUREC_ERROR_INVALID_RANGE("strcat_s");
199 return ERANGE;
200 }
201
202 if (strDest == NULL || strSrc == NULL) {
203 SECUREC_ERROR_INVALID_PARAMTER("strcat_s");
204 if (strDest != NULL) {
205 strDest[0] = '\0';
206 return EINVAL_AND_RESET;
207 }
208 return EINVAL;
209 }
210
211 return SecDoStrcat(strDest, destMax, strSrc);
212 }
213
214 #if SECUREC_IN_KERNEL
215 EXPORT_SYMBOL(strcat_s);
216 #endif
217
218