blob: 0b187672816fbec5ddf3409c54d3ae5637d63e51 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
Damjan Marionf1213b82016-03-13 02:22:06 +01002 * Copyright (c) 2016 Cisco and/or its affiliates.
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/*
Ed Warnickecb9cada2015-12-08 15:45:58 -070016 Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
Dave Barachb7b92992018-10-17 10:38:51 -040038/** \file
39
40 Optimized string handling code, including c11-compliant
41 "safe C library" variants.
42*/
43
Ed Warnickecb9cada2015-12-08 15:45:58 -070044#ifndef included_clib_string_h
45#define included_clib_string_h
46
Dave Barachc3799992016-08-15 11:12:27 -040047#include <vppinfra/clib.h> /* for CLIB_LINUX_KERNEL */
Damjan Marione319de02016-10-21 19:30:42 +020048#include <vppinfra/vector.h>
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010049#include <vppinfra/error_bootstrap.h>
Damjan Marion56f54af2021-10-12 20:30:02 +020050#include <vppinfra/memcpy_x86_64.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070051
52#ifdef CLIB_LINUX_KERNEL
53#include <linux/string.h>
54#endif
55
56#ifdef CLIB_UNIX
57#include <string.h>
58#endif
59
60#ifdef CLIB_STANDALONE
61#include <vppinfra/standalone_string.h>
62#endif
63
Damjan Marionb2e1fe92017-11-22 12:41:32 +010064#if _x86_64_
65#include <x86intrin.h>
66#endif
67
Ed Warnickecb9cada2015-12-08 15:45:58 -070068/* Exchanges source and destination. */
Dave Barachc3799992016-08-15 11:12:27 -040069void clib_memswap (void *_a, void *_b, uword bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -070070
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010071
72static_always_inline void *
73clib_memcpy_fast (void *restrict dst, const void *restrict src, size_t n)
74{
75 ASSERT (dst && src &&
76 "memcpy(src, dst, n) with src == NULL or dst == NULL is undefined "
77 "behaviour");
Damjan Marion56f54af2021-10-12 20:30:02 +020078#if defined(__COVERITY__)
79 return memcpy (dst, src, n);
80#elif defined(__x86_64__)
81 clib_memcpy_x86_64 (dst, src, n);
82 return dst;
83#else
84 return memcpy (dst, src, n);
85#endif
Benoît Ganne1a3e08a2021-02-11 19:46:43 +010086}
87
Damjan Marion856d0622021-04-21 21:11:35 +020088#include <vppinfra/memcpy.h>
89
Dave Barachb7b92992018-10-17 10:38:51 -040090/* c-11 string manipulation variants */
91
92#ifndef EOK
93#define EOK 0
94#endif
95#ifndef EINVAL
96#define EINVAL 22
97#endif
Stevenb0598492018-10-24 21:15:45 -070098#ifndef ESRCH
99#define ESRCH 3
100#endif
101#ifndef EOVERFLOW
102#define EOVERFLOW 75
103#endif
104
105/*
106 * In order to provide smooth mapping from unsafe string API to the clib string
107 * macro, we often have to improvise s1max and s2max due to the additional
108 * arguments are required for implementing the safe API. This macro is used
Stevenf09179f2019-01-07 20:32:01 -0800109 * to provide the s1max/s2max. It is not perfect because the actual
Stevenb0598492018-10-24 21:15:45 -0700110 * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
111 * the macro would cause a regression. However, it is not terribly likely.
112 * So I bet against the odds.
113 */
114#define CLIB_STRING_MACRO_MAX 4096
Dave Barachb7b92992018-10-17 10:38:51 -0400115
116typedef int errno_t;
117typedef uword rsize_t;
118
119void clib_c11_violation (const char *s);
120errno_t memcpy_s (void *__restrict__ dest, rsize_t dmax,
121 const void *__restrict__ src, rsize_t n);
122
123always_inline errno_t
124memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
125 const void *__restrict__ src, rsize_t n)
126{
127 uword low, hi;
128 u8 bad;
129
130 /*
Dave Barach178cf492018-11-13 16:34:13 -0500131 * Optimize constant-number-of-bytes calls without asking
132 * "too many questions for someone from New Jersey"
133 */
Damjan Marionb14c49d2021-04-25 10:55:53 +0200134 if (COMPILE_TIME_CONST (n))
Dave Barach178cf492018-11-13 16:34:13 -0500135 {
136 clib_memcpy_fast (dest, src, n);
137 return EOK;
138 }
139
140 /*
Dave Barachb7b92992018-10-17 10:38:51 -0400141 * call bogus if: src or dst NULL, trying to copy
142 * more data than we have space in dst, or src == dst.
143 * n == 0 isn't really "bad", so check first in the
144 * "wall-of-shame" department...
145 */
146 bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
147 if (PREDICT_FALSE (bad != 0))
148 {
149 /* Not actually trying to copy anything is OK */
150 if (n == 0)
151 return EOK;
152 if (dest == NULL)
153 clib_c11_violation ("dest NULL");
154 if (src == NULL)
155 clib_c11_violation ("src NULL");
156 if (n > dmax)
157 clib_c11_violation ("n > dmax");
158 if (dest == src)
159 clib_c11_violation ("dest == src");
160 return EINVAL;
161 }
162
163 /* Check for src/dst overlap, which is not allowed */
164 low = (uword) (src < dest ? src : dest);
165 hi = (uword) (src < dest ? dest : src);
166
167 if (PREDICT_FALSE (low + (n - 1) >= hi))
168 {
169 clib_c11_violation ("src/dest overlap");
170 return EINVAL;
171 }
172
Dave Barach178cf492018-11-13 16:34:13 -0500173 clib_memcpy_fast (dest, src, n);
Dave Barachb7b92992018-10-17 10:38:51 -0400174 return EOK;
175}
176
177/*
178 * Note: $$$ This macro is a crutch. Folks need to manually
179 * inspect every extant clib_memcpy(...) call and
180 * attempt to provide a real destination buffer size
181 * argument...
182 */
183#define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
184
185errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
186
187always_inline errno_t
188memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
189{
190 u8 bad;
191
192 bad = (s == 0) + (n > smax);
193
194 if (PREDICT_FALSE (bad != 0))
195 {
196 if (s == 0)
197 clib_c11_violation ("s NULL");
198 if (n > smax)
199 clib_c11_violation ("n > smax");
200 return (EINVAL);
201 }
202 memset (s, c, n);
203 return (EOK);
204}
205
206/*
207 * This macro is not [so much of] a crutch.
208 * It's super-typical to write:
209 *
210 * ep = pool_get (<pool>);
211 * clib_memset(ep, 0, sizeof (*ep));
212 *
213 * The compiler should delete the not-so useful
214 * (n > smax) test. TBH the NULL pointer check isn't
215 * so useful in this case, but so be it.
216 */
217#define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
218
Damjan Marion14864772018-05-22 14:07:47 +0200219static_always_inline void
Damjan Marionc59b9a22019-03-19 15:38:40 +0100220clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
221{
Igor Mikhailov (imichail)419e15f2019-05-13 12:04:04 -0700222#if defined (CLIB_HAVE_VEC256)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100223 u8x32 s0, s1, d0, d1;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100224 u8x32 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
225 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
226 };
227 u8x32 lv = u8x32_splat (len);
228 u8x32 add = u8x32_splat (32);
229
Damjan Marion4d3aa072019-03-28 16:19:24 +0100230 s0 = u8x32_load_unaligned (src);
231 s1 = u8x32_load_unaligned (src + 32);
232 d0 = u8x32_load_unaligned (dst);
233 d1 = u8x32_load_unaligned (dst + 32);
234
Damjan Marione01ce5a2022-02-10 15:23:15 +0100235 d0 = u8x32_blend (d0, s0, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100236 u8x32_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100237
238 if (max_len <= 32)
239 return;
240
241 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100242 d1 = u8x32_blend (d1, s1, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100243 u8x32_store_unaligned (d1, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100244
Lijian.Zhang37c83782019-04-04 15:26:26 +0800245#elif defined (CLIB_HAVE_VEC128)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100246 u8x16 s0, s1, s2, s3, d0, d1, d2, d3;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100247 u8x16 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
248 u8x16 lv = u8x16_splat (len);
249 u8x16 add = u8x16_splat (16);
250
Damjan Marion4d3aa072019-03-28 16:19:24 +0100251 s0 = u8x16_load_unaligned (src);
252 s1 = u8x16_load_unaligned (src + 16);
253 s2 = u8x16_load_unaligned (src + 32);
254 s3 = u8x16_load_unaligned (src + 48);
255 d0 = u8x16_load_unaligned (dst);
256 d1 = u8x16_load_unaligned (dst + 16);
257 d2 = u8x16_load_unaligned (dst + 32);
258 d3 = u8x16_load_unaligned (dst + 48);
259
Damjan Marione01ce5a2022-02-10 15:23:15 +0100260 d0 = u8x16_blend (d0, s0, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100261 u8x16_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100262
263 if (max_len <= 16)
264 return;
265
266 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100267 d1 = u8x16_blend (d1, s1, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100268 u8x16_store_unaligned (d1, dst + 16);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100269
270 if (max_len <= 32)
271 return;
272
273 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100274 d2 = u8x16_blend (d2, s2, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100275 u8x16_store_unaligned (d2, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100276
277 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100278 d3 = u8x16_blend (d3, s3, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100279 u8x16_store_unaligned (d3, dst + 48);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100280#else
Damjan Marion4d3aa072019-03-28 16:19:24 +0100281 memmove (dst, src, len);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100282#endif
283}
284
285static_always_inline void
286clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
287{
288 clib_memcpy_le (dst, src, len, 64);
289}
290
291static_always_inline void
292clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
293{
294 clib_memcpy_le (dst, src, len, 32);
295}
296
297static_always_inline void
Damjan Marion14864772018-05-22 14:07:47 +0200298clib_memset_u64 (void *p, u64 val, uword count)
299{
300 u64 *ptr = p;
301#if defined(CLIB_HAVE_VEC512)
302 u64x8 v512 = u64x8_splat (val);
303 while (count >= 8)
304 {
305 u64x8_store_unaligned (v512, ptr);
306 ptr += 8;
307 count -= 8;
308 }
309 if (count == 0)
310 return;
311#endif
312#if defined(CLIB_HAVE_VEC256)
313 u64x4 v256 = u64x4_splat (val);
314 while (count >= 4)
315 {
316 u64x4_store_unaligned (v256, ptr);
317 ptr += 4;
318 count -= 4;
319 }
320 if (count == 0)
321 return;
322#else
Damjan Marion137d4ca2022-01-31 17:42:44 +0100323#if defined(CLIB_HAVE_VEC128)
324 u64x2 v = u64x2_splat (val);
325#endif
Damjan Marion14864772018-05-22 14:07:47 +0200326 while (count >= 4)
327 {
Damjan Marion137d4ca2022-01-31 17:42:44 +0100328#if defined(CLIB_HAVE_VEC128)
329 u64x2_store_unaligned (v, ptr);
330 u64x2_store_unaligned (v, ptr + 2);
331#else
Damjan Marion14864772018-05-22 14:07:47 +0200332 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
Damjan Marion137d4ca2022-01-31 17:42:44 +0100333#endif
Damjan Marion14864772018-05-22 14:07:47 +0200334 ptr += 4;
335 count -= 4;
336 }
337#endif
338 while (count--)
339 ptr++[0] = val;
340}
341
342static_always_inline void
343clib_memset_u32 (void *p, u32 val, uword count)
344{
345 u32 *ptr = p;
346#if defined(CLIB_HAVE_VEC512)
347 u32x16 v512 = u32x16_splat (val);
348 while (count >= 16)
349 {
350 u32x16_store_unaligned (v512, ptr);
351 ptr += 16;
352 count -= 16;
353 }
354 if (count == 0)
355 return;
356#endif
357#if defined(CLIB_HAVE_VEC256)
358 u32x8 v256 = u32x8_splat (val);
359 while (count >= 8)
360 {
361 u32x8_store_unaligned (v256, ptr);
362 ptr += 8;
363 count -= 8;
364 }
365 if (count == 0)
366 return;
367#endif
368#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
369 u32x4 v128 = u32x4_splat (val);
370 while (count >= 4)
371 {
372 u32x4_store_unaligned (v128, ptr);
373 ptr += 4;
374 count -= 4;
375 }
376#else
377 while (count >= 4)
378 {
379 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
380 ptr += 4;
381 count -= 4;
382 }
383#endif
384 while (count--)
385 ptr++[0] = val;
386}
387
388static_always_inline void
389clib_memset_u16 (void *p, u16 val, uword count)
390{
391 u16 *ptr = p;
392#if defined(CLIB_HAVE_VEC512)
393 u16x32 v512 = u16x32_splat (val);
394 while (count >= 32)
395 {
396 u16x32_store_unaligned (v512, ptr);
397 ptr += 32;
398 count -= 32;
399 }
400 if (count == 0)
401 return;
402#endif
403#if defined(CLIB_HAVE_VEC256)
404 u16x16 v256 = u16x16_splat (val);
405 while (count >= 16)
406 {
407 u16x16_store_unaligned (v256, ptr);
408 ptr += 16;
409 count -= 16;
410 }
411 if (count == 0)
412 return;
413#endif
414#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
415 u16x8 v128 = u16x8_splat (val);
416 while (count >= 8)
417 {
418 u16x8_store_unaligned (v128, ptr);
419 ptr += 8;
420 count -= 8;
421 }
422#else
423 while (count >= 4)
424 {
425 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
426 ptr += 4;
427 count -= 4;
428 }
429#endif
430 while (count--)
431 ptr++[0] = val;
432}
433
434static_always_inline void
435clib_memset_u8 (void *p, u8 val, uword count)
436{
437 u8 *ptr = p;
438#if defined(CLIB_HAVE_VEC512)
439 u8x64 v512 = u8x64_splat (val);
440 while (count >= 64)
441 {
442 u8x64_store_unaligned (v512, ptr);
443 ptr += 64;
444 count -= 64;
445 }
446 if (count == 0)
447 return;
448#endif
449#if defined(CLIB_HAVE_VEC256)
450 u8x32 v256 = u8x32_splat (val);
451 while (count >= 32)
452 {
453 u8x32_store_unaligned (v256, ptr);
454 ptr += 32;
455 count -= 32;
456 }
457 if (count == 0)
458 return;
459#endif
460#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
461 u8x16 v128 = u8x16_splat (val);
462 while (count >= 16)
463 {
464 u8x16_store_unaligned (v128, ptr);
465 ptr += 16;
466 count -= 16;
467 }
468#else
469 while (count >= 4)
470 {
471 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
472 ptr += 4;
473 count -= 4;
474 }
475#endif
476 while (count--)
477 ptr++[0] = val;
478}
479
Damjan Marion14864772018-05-22 14:07:47 +0200480
Stevenb0598492018-10-24 21:15:45 -0700481/*
482 * This macro is to provide smooth mapping from memcmp to memcmp_s.
483 * memcmp has fewer parameters and fewer returns than memcmp_s.
484 * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
485 * we return 0 and spit out a message in the console because there is
486 * no way to return the error code to the memcmp callers.
487 * This condition happens when s1 or s2 is null. Please note
488 * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
489 * anyway. So we are consistent in this case for the comparison return
490 * although we also spit out a C11 violation message in the console to
491 * warn that they pass null pointers for both s1 and s2.
492 * Applications are encouraged to use the cool C11 memcmp_s API to get the
493 * maximum benefit out of it.
494 */
495#define clib_memcmp(s1,s2,m1) \
496 ({ int __diff = 0; \
497 memcmp_s_inline (s1, m1, s2, m1, &__diff); \
498 __diff; \
499 })
500
501errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
502 rsize_t s2max, int *diff);
503
504always_inline errno_t
505memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
506 int *diff)
507{
508 u8 bad;
509
510 bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
511 (s1max == 0);
512
513 if (PREDICT_FALSE (bad != 0))
514 {
515 if (s1 == NULL)
516 clib_c11_violation ("s1 NULL");
517 if (s2 == NULL)
518 clib_c11_violation ("s2 NULL");
519 if (diff == NULL)
520 clib_c11_violation ("diff NULL");
521 if (s2max > s1max)
522 clib_c11_violation ("s2max > s1max");
523 if (s2max == 0)
524 clib_c11_violation ("s2max 0");
525 if (s1max == 0)
526 clib_c11_violation ("s1max 0");
527 return EINVAL;
528 }
529
530 if (PREDICT_FALSE (s1 == s2))
531 {
532 *diff = 0;
533 return EOK;
534 }
535
536 *diff = memcmp (s1, s2, s2max);
537 return EOK;
538}
539
540/*
541 * This macro is to provide smooth mapping from strnlen to strnlen_s
542 */
543#define clib_strnlen(s,m) strnlen_s_inline(s,m)
544
545size_t strnlen_s (const char *s, size_t maxsize);
546
547always_inline size_t
548strnlen_s_inline (const char *s, size_t maxsize)
549{
550 u8 bad;
551
552 bad = (s == 0) + (maxsize == 0);
553 if (PREDICT_FALSE (bad != 0))
554 {
555 if (s == 0)
556 clib_c11_violation ("s NULL");
557 if (maxsize == 0)
558 clib_c11_violation ("maxsize 0");
559 return 0;
560 }
561 return strnlen (s, maxsize);
562}
563
564/*
565 * This macro is to provide smooth mapping from strcmp to strcmp_s.
566 * strcmp has fewer parameters and fewer returns than strcmp_s.
567 * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
568 * we return 0 and spit out a message in the console because
569 * there is no way to return the error to the strcmp callers.
570 * This condition happens when s1 or s2 is null. Please note in the extant
571 * strcmp call, they would end up crashing if one of them is null.
572 * So the new behavior is no crash, but an error is displayed in the
573 * console which I think is more user friendly. If both s1 and s2 are null,
574 * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
575 * to actually accessing the pointer contents. We are still consistent
576 * in this case for the comparison return although we also spit out a
577 * C11 violation message in the console to warn that they pass null pointers
578 * for both s1 and s2. The other problem is strcmp does not provide s1max,
579 * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
580 * If not, we may be accessing memory beyonf what is intended.
581 * Applications are encouraged to use the cool C11 strcmp_s API to get the
582 * maximum benefit out of it.
583 */
584#define clib_strcmp(s1,s2) \
585 ({ int __indicator = 0; \
586 strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
587 __indicator; \
588 })
589
590errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
591 int *indicator);
592
593always_inline errno_t
594strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
595 int *indicator)
596{
597 u8 bad;
598
599 bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
600 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
601
602 if (PREDICT_FALSE (bad != 0))
603 {
604 if (indicator == NULL)
605 clib_c11_violation ("indicator NULL");
606 if (s1 == NULL)
607 clib_c11_violation ("s1 NULL");
608 if (s2 == NULL)
609 clib_c11_violation ("s2 NULL");
610 if (s1max == 0)
611 clib_c11_violation ("s1max 0");
612 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
613 clib_c11_violation ("s1 unterminated");
614 return EINVAL;
615 }
616
617 *indicator = strcmp (s1, s2);
618 return EOK;
619}
620
621/*
622 * This macro is to provide smooth mapping from strncmp to strncmp_s.
623 * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
624 * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
625 * we return 0 and spit out a message in the console because there is no
626 * means to return the error to the strncmp caller.
627 * This condition happens when s1 or s2 is null. In the extant strncmp call,
628 * they would end up crashing if one of them is null. So the new behavior is
629 * no crash, but error is displayed in the console which is more
630 * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
631 * strncmp did the pointers comparison prior to actually accessing the
632 * pointer contents. We are still consistent in this case for the comparison
633 * return although we also spit out a C11 violation message in the console to
634 * warn that they pass null pointers for both s1 and s2.
635 * Applications are encouraged to use the cool C11 strncmp_s API to get the
636 * maximum benefit out of it.
637 */
638#define clib_strncmp(s1,s2,n) \
639 ({ int __indicator = 0; \
640 strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
641 __indicator; \
642 })
643
644errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
645 int *indicator);
646
647always_inline errno_t
648strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
649 int *indicator)
650{
651 u8 bad;
652 u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
653
654 if (PREDICT_FALSE (s1_greater_s1max && indicator))
655 {
656 /*
657 * strcmp allows n > s1max. If indicator is non null, we can still
658 * do the compare without any harm and return EINVAL as well as the
659 * result in indicator.
660 */
661 clib_c11_violation ("n exceeds s1 length");
662 *indicator = strncmp (s1, s2, n);
663 return EINVAL;
664 }
665
666 bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
667 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
668
669 if (PREDICT_FALSE (bad != 0))
670 {
671 if (indicator == NULL)
672 clib_c11_violation ("indicator NULL");
673 if (s1 == NULL)
674 clib_c11_violation ("s1 NULL");
675 if (s2 == NULL)
676 clib_c11_violation ("s2 NULL");
677 if (s1max == 0)
678 clib_c11_violation ("s1max 0");
679 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
680 clib_c11_violation ("s1 unterminated");
681 if (s1_greater_s1max)
682 clib_c11_violation ("n exceeds s1 length");
683 return EINVAL;
684 }
685
686 *indicator = strncmp (s1, s2, n);
687 return EOK;
688}
689
Stevenb0598492018-10-24 21:15:45 -0700690errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
691 const char *__restrict__ src);
692
693always_inline errno_t
694strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
695 const char *__restrict__ src)
696{
697 u8 bad;
698 uword low, hi;
699 size_t n;
700
701 bad = (dest == 0) + (dmax == 0) + (src == 0);
702 if (PREDICT_FALSE (bad != 0))
703 {
704 if (dest == 0)
705 clib_c11_violation ("dest NULL");
706 if (src == 0)
707 clib_c11_violation ("src NULL");
708 if (dmax == 0)
709 clib_c11_violation ("dmax 0");
710 return EINVAL;
711 }
712
713 n = clib_strnlen (src, dmax);
714 if (PREDICT_FALSE (n >= dmax))
715 {
716 clib_c11_violation ("not enough space for dest");
717 return (EINVAL);
718 }
719 /* Not actually trying to copy anything is OK */
720 if (PREDICT_FALSE (n == 0))
721 return EOK;
722
723 /* Check for src/dst overlap, which is not allowed */
724 low = (uword) (src < dest ? src : dest);
725 hi = (uword) (src < dest ? dest : src);
726
727 if (PREDICT_FALSE (low + (n - 1) >= hi))
728 {
729 clib_c11_violation ("src/dest overlap");
730 return EINVAL;
731 }
732
733 clib_memcpy_fast (dest, src, n);
734 dest[n] = '\0';
735 return EOK;
736}
737
738/*
739 * This macro is provided for smooth migration from strncpy. It is not perfect
740 * because we don't know the size of the destination buffer to pass to
741 * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
742 * Applications are encouraged to move to the C11 strncpy_s API and provide
743 * the correct dmax for better error checking.
744 */
745#define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
746
747errno_t
748strncpy_s (char *__restrict__ dest, rsize_t dmax,
749 const char *__restrict__ src, rsize_t n);
750
751always_inline errno_t
752strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
753 const char *__restrict__ src, rsize_t n)
754{
755 u8 bad;
756 uword low, hi;
757 rsize_t m;
758 errno_t status = EOK;
759
760 bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
761 if (PREDICT_FALSE (bad != 0))
762 {
763 /* Not actually trying to copy anything is OK */
764 if (n == 0)
765 return EOK;
766 if (dest == 0)
767 clib_c11_violation ("dest NULL");
768 if (src == 0)
769 clib_c11_violation ("src NULL");
770 if (dmax == 0)
771 clib_c11_violation ("dmax 0");
772 return EINVAL;
773 }
774
775 if (PREDICT_FALSE (n >= dmax))
776 {
777 /* Relax and use strnlen of src */
778 clib_c11_violation ("n >= dmax");
779 m = clib_strnlen (src, dmax);
780 if (m >= dmax)
781 {
782 /* Truncate, adjust copy length to fit dest */
783 m = dmax - 1;
784 status = EOVERFLOW;
785 }
786 }
787 else
Stevenf09179f2019-01-07 20:32:01 -0800788 /* cap the copy to strlen(src) in case n > strlen(src) */
789 m = clib_strnlen (src, n);
Stevenb0598492018-10-24 21:15:45 -0700790
791 /* Check for src/dst overlap, which is not allowed */
792 low = (uword) (src < dest ? src : dest);
793 hi = (uword) (src < dest ? dest : src);
794
Dave Barachd08ae852018-12-05 08:41:11 -0500795 /*
796 * This check may fail innocently if src + dmax >= dst, but
797 * src + strlen(src) < dst. If it fails, check more carefully before
798 * blowing the whistle.
799 */
Stevenb0598492018-10-24 21:15:45 -0700800 if (PREDICT_FALSE (low + (m - 1) >= hi))
801 {
Dave Barachd08ae852018-12-05 08:41:11 -0500802 m = clib_strnlen (src, m);
803
804 if (low + (m - 1) >= hi)
805 {
806 clib_c11_violation ("src/dest overlap");
807 return EINVAL;
808 }
Stevenb0598492018-10-24 21:15:45 -0700809 }
810
811 clib_memcpy_fast (dest, src, m);
812 dest[m] = '\0';
813 return status;
814}
815
Stevenb0598492018-10-24 21:15:45 -0700816errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
817 const char *__restrict__ src);
818
819always_inline errno_t
820strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
821 const char *__restrict__ src)
822{
823 u8 bad;
824 uword low, hi;
825 size_t m, n, dest_size;
826
827 bad = (dest == 0) + (dmax == 0) + (src == 0);
828 if (PREDICT_FALSE (bad != 0))
829 {
830 if (dest == 0)
831 clib_c11_violation ("dest NULL");
832 if (src == 0)
833 clib_c11_violation ("src NULL");
834 if (dmax == 0)
835 clib_c11_violation ("dmax 0");
836 return EINVAL;
837 }
838
839 dest_size = clib_strnlen (dest, dmax);
840 m = dmax - dest_size;
841 n = clib_strnlen (src, m);
842 if (PREDICT_FALSE (n >= m))
843 {
844 clib_c11_violation ("not enough space for dest");
845 return EINVAL;
846 }
847
848 /* Not actually trying to concatenate anything is OK */
849 if (PREDICT_FALSE (n == 0))
850 return EOK;
851
852 /* Check for src/dst overlap, which is not allowed */
853 low = (uword) (src < dest ? src : dest);
854 hi = (uword) (src < dest ? dest : src);
855
856 if (PREDICT_FALSE (low + (n - 1) >= hi))
857 {
858 clib_c11_violation ("src/dest overlap");
859 return EINVAL;
860 }
861
862 clib_memcpy_fast (dest + dest_size, src, n);
863 dest[dest_size + n] = '\0';
864 return EOK;
865}
866
Stevenb0598492018-10-24 21:15:45 -0700867errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
868 const char *__restrict__ src, rsize_t n);
869
870always_inline errno_t
871strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
872 const char *__restrict__ src, rsize_t n)
873{
874 u8 bad;
875 uword low, hi;
876 size_t m, dest_size, allowed_size;
877 errno_t status = EOK;
878
879 bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
880 if (PREDICT_FALSE (bad != 0))
881 {
882 /* Not actually trying to concatenate anything is OK */
883 if (n == 0)
884 return EOK;
885 if (dest == 0)
886 clib_c11_violation ("dest NULL");
887 if (src == 0)
888 clib_c11_violation ("src NULL");
889 if (dmax == 0)
890 clib_c11_violation ("dmax 0");
891 return EINVAL;
892 }
893
894 /* Check for src/dst overlap, which is not allowed */
895 low = (uword) (src < dest ? src : dest);
896 hi = (uword) (src < dest ? dest : src);
897
898 if (PREDICT_FALSE (low + (n - 1) >= hi))
899 {
900 clib_c11_violation ("src/dest overlap");
901 return EINVAL;
902 }
903
904 dest_size = clib_strnlen (dest, dmax);
905 allowed_size = dmax - dest_size;
906
907 if (PREDICT_FALSE (allowed_size == 0))
908 {
909 clib_c11_violation ("no space left in dest");
910 return (EINVAL);
911 }
912
913 if (PREDICT_FALSE (n >= allowed_size))
914 {
915 /*
916 * unlike strcat_s, strncat_s will do the concatenation anyway when
917 * there is not enough space in dest. But it will do the truncation and
918 * null terminate dest
919 */
920 m = clib_strnlen (src, allowed_size);
921 if (m >= allowed_size)
922 {
923 m = allowed_size - 1;
924 status = EOVERFLOW;
925 }
926 }
927 else
928 m = clib_strnlen (src, n);
929
930 clib_memcpy_fast (dest + dest_size, src, m);
931 dest[dest_size + m] = '\0';
932 return status;
933}
934
935/*
936 * This macro is to provide smooth mapping from strtok_r to strtok_s.
937 * To map strtok to this macro, the caller would have to supply an additional
938 * argument. strtokr_s requires s1max which the unsafe API does not have. So
939 * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
940 * this macro cannot catch unterminated s1 and s2.
941 * Applications are encouraged to use the cool C11 strtok_s API to avoid
942 * these problems.
943 */
944#define clib_strtok(s1,s2,p) \
945 ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
946 strtok_s_inline (s1, &__s1max, s2, p); \
947 })
948
949char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
950 const char *__restrict__ s2, char **__restrict__ ptr);
951
952always_inline char *
953strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
954 const char *__restrict__ s2, char **__restrict__ ptr)
955{
956#define STRTOK_DELIM_MAX_LEN 16
957 u8 bad;
958 const char *pt;
959 char *ptoken;
960 uword dlen, slen;
961
962 bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
963 ((s1 == 0) && ptr && (*ptr == 0));
964 if (PREDICT_FALSE (bad != 0))
965 {
966 if (s2 == NULL)
967 clib_c11_violation ("s2 NULL");
968 if (s1max == NULL)
969 clib_c11_violation ("s1max is NULL");
970 if (ptr == NULL)
971 clib_c11_violation ("ptr is NULL");
972 /* s1 == 0 and *ptr == null is no good */
973 if ((s1 == 0) && ptr && (*ptr == 0))
974 clib_c11_violation ("s1 and ptr contents are NULL");
975 return 0;
976 }
977
978 if (s1 == 0)
979 s1 = *ptr;
980
981 /*
982 * scan s1 for a delimiter
983 */
984 dlen = *s1max;
985 ptoken = 0;
986 while (*s1 != '\0' && !ptoken)
987 {
988 if (PREDICT_FALSE (dlen == 0))
989 {
990 *ptr = 0;
991 clib_c11_violation ("s1 unterminated");
992 return 0;
993 }
994
995 /*
996 * must scan the entire delimiter list
997 * ISO should have included a delimiter string limit!!
998 */
999 slen = STRTOK_DELIM_MAX_LEN;
1000 pt = s2;
1001 while (*pt != '\0')
1002 {
1003 if (PREDICT_FALSE (slen == 0))
1004 {
1005 *ptr = 0;
1006 clib_c11_violation ("s2 unterminated");
1007 return 0;
1008 }
1009 slen--;
1010 if (*s1 == *pt)
1011 {
1012 ptoken = 0;
1013 break;
1014 }
1015 else
1016 {
1017 pt++;
1018 ptoken = s1;
1019 }
1020 }
1021 s1++;
1022 dlen--;
1023 }
1024
1025 /*
1026 * if the beginning of a token was not found, then no
1027 * need to continue the scan.
1028 */
1029 if (ptoken == 0)
1030 {
1031 *s1max = dlen;
1032 return (ptoken);
1033 }
1034
1035 /*
1036 * Now we need to locate the end of the token
1037 */
1038 while (*s1 != '\0')
1039 {
1040 if (dlen == 0)
1041 {
1042 *ptr = 0;
1043 clib_c11_violation ("s1 unterminated");
1044 return 0;
1045 }
1046
1047 slen = STRTOK_DELIM_MAX_LEN;
1048 pt = s2;
1049 while (*pt != '\0')
1050 {
1051 if (slen == 0)
1052 {
1053 *ptr = 0;
1054 clib_c11_violation ("s2 unterminated");
1055 return 0;
1056 }
1057 slen--;
1058 if (*s1 == *pt)
1059 {
1060 /*
1061 * found a delimiter, set to null
1062 * and return context ptr to next char
1063 */
1064 *s1 = '\0';
1065 *ptr = (s1 + 1); /* return pointer for next scan */
1066 *s1max = dlen - 1; /* account for the nulled delimiter */
1067 return (ptoken);
1068 }
1069 else
1070 {
1071 /*
1072 * simply scanning through the delimiter string
1073 */
1074 pt++;
1075 }
1076 }
1077 s1++;
1078 dlen--;
1079 }
1080
1081 *ptr = s1;
1082 *s1max = dlen;
1083 return (ptoken);
1084}
1085
Stevenb0598492018-10-24 21:15:45 -07001086errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1087 char **substring);
1088
1089always_inline errno_t
1090strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1091 char **substring)
1092{
1093 u8 bad;
1094 size_t s1_size, s2_size;
1095
1096 bad =
1097 (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
1098 (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
1099 (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
1100 if (PREDICT_FALSE (bad != 0))
1101 {
1102 if (s1 == 0)
1103 clib_c11_violation ("s1 NULL");
1104 if (s2 == 0)
1105 clib_c11_violation ("s2 NULL");
1106 if (s1max == 0)
1107 clib_c11_violation ("s1max 0");
1108 if (s2max == 0)
1109 clib_c11_violation ("s2max 0");
1110 if (substring == 0)
1111 clib_c11_violation ("substring NULL");
1112 if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
1113 clib_c11_violation ("s1 unterminated");
Steven Luongb5a2b052021-11-03 16:49:04 -07001114 if (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'))
Stevenb0598492018-10-24 21:15:45 -07001115 clib_c11_violation ("s2 unterminated");
1116 return EINVAL;
1117 }
1118
1119 /*
1120 * s2 points to a string with zero length, or s2 equals s1, return s1
1121 */
1122 if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1123 {
1124 *substring = s1;
1125 return EOK;
1126 }
1127
1128 /*
1129 * s2_size > s1_size, it won't find match.
1130 */
1131 s1_size = clib_strnlen (s1, s1max);
1132 s2_size = clib_strnlen (s2, s2max);
1133 if (PREDICT_FALSE (s2_size > s1_size))
1134 return ESRCH;
1135
1136 *substring = strstr (s1, s2);
1137 if (*substring == 0)
1138 return ESRCH;
1139
1140 return EOK;
1141}
1142
Ed Warnickecb9cada2015-12-08 15:45:58 -07001143#endif /* included_clib_string_h */
Dave Barachc3799992016-08-15 11:12:27 -04001144
1145/*
1146 * fd.io coding-style-patch-verification: ON
1147 *
1148 * Local Variables:
1149 * eval: (c-set-style "gnu")
1150 * End:
1151 */