blob: 0d8b1e6cb257579fee88b015b3aa1c183f3e6f19 [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 Marion299571a2022-03-19 00:07:52 +010088static_always_inline void *
89clib_memmove (void *dst, const void *src, size_t n)
90{
91 u8 *d = (u8 *) dst;
92 u8 *s = (u8 *) src;
93
94 if (s == d)
95 return d;
96
97 if (d > s)
98 for (uword i = n - 1; (i + 1) > 0; i--)
99 d[i] = s[i];
100 else
101 for (uword i = 0; i < n; i++)
102 d[i] = s[i];
103
104 return d;
105}
106
Damjan Marion856d0622021-04-21 21:11:35 +0200107#include <vppinfra/memcpy.h>
108
Dave Barachb7b92992018-10-17 10:38:51 -0400109/* c-11 string manipulation variants */
110
111#ifndef EOK
112#define EOK 0
113#endif
114#ifndef EINVAL
115#define EINVAL 22
116#endif
Stevenb0598492018-10-24 21:15:45 -0700117#ifndef ESRCH
118#define ESRCH 3
119#endif
120#ifndef EOVERFLOW
121#define EOVERFLOW 75
122#endif
123
124/*
125 * In order to provide smooth mapping from unsafe string API to the clib string
126 * macro, we often have to improvise s1max and s2max due to the additional
127 * arguments are required for implementing the safe API. This macro is used
Stevenf09179f2019-01-07 20:32:01 -0800128 * to provide the s1max/s2max. It is not perfect because the actual
Stevenb0598492018-10-24 21:15:45 -0700129 * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
130 * the macro would cause a regression. However, it is not terribly likely.
131 * So I bet against the odds.
132 */
133#define CLIB_STRING_MACRO_MAX 4096
Dave Barachb7b92992018-10-17 10:38:51 -0400134
135typedef int errno_t;
136typedef uword rsize_t;
137
138void clib_c11_violation (const char *s);
139errno_t memcpy_s (void *__restrict__ dest, rsize_t dmax,
140 const void *__restrict__ src, rsize_t n);
141
142always_inline errno_t
143memcpy_s_inline (void *__restrict__ dest, rsize_t dmax,
144 const void *__restrict__ src, rsize_t n)
145{
146 uword low, hi;
147 u8 bad;
148
149 /*
Dave Barach178cf492018-11-13 16:34:13 -0500150 * Optimize constant-number-of-bytes calls without asking
151 * "too many questions for someone from New Jersey"
152 */
Damjan Marionb14c49d2021-04-25 10:55:53 +0200153 if (COMPILE_TIME_CONST (n))
Dave Barach178cf492018-11-13 16:34:13 -0500154 {
155 clib_memcpy_fast (dest, src, n);
156 return EOK;
157 }
158
159 /*
Dave Barachb7b92992018-10-17 10:38:51 -0400160 * call bogus if: src or dst NULL, trying to copy
161 * more data than we have space in dst, or src == dst.
162 * n == 0 isn't really "bad", so check first in the
163 * "wall-of-shame" department...
164 */
165 bad = (dest == 0) + (src == 0) + (n > dmax) + (dest == src) + (n == 0);
166 if (PREDICT_FALSE (bad != 0))
167 {
168 /* Not actually trying to copy anything is OK */
169 if (n == 0)
170 return EOK;
171 if (dest == NULL)
172 clib_c11_violation ("dest NULL");
173 if (src == NULL)
174 clib_c11_violation ("src NULL");
175 if (n > dmax)
176 clib_c11_violation ("n > dmax");
177 if (dest == src)
178 clib_c11_violation ("dest == src");
179 return EINVAL;
180 }
181
182 /* Check for src/dst overlap, which is not allowed */
183 low = (uword) (src < dest ? src : dest);
184 hi = (uword) (src < dest ? dest : src);
185
186 if (PREDICT_FALSE (low + (n - 1) >= hi))
187 {
188 clib_c11_violation ("src/dest overlap");
189 return EINVAL;
190 }
191
Dave Barach178cf492018-11-13 16:34:13 -0500192 clib_memcpy_fast (dest, src, n);
Dave Barachb7b92992018-10-17 10:38:51 -0400193 return EOK;
194}
195
196/*
197 * Note: $$$ This macro is a crutch. Folks need to manually
198 * inspect every extant clib_memcpy(...) call and
199 * attempt to provide a real destination buffer size
200 * argument...
201 */
202#define clib_memcpy(d,s,n) memcpy_s_inline(d,n,s,n)
203
204errno_t memset_s (void *s, rsize_t smax, int c, rsize_t n);
205
206always_inline errno_t
207memset_s_inline (void *s, rsize_t smax, int c, rsize_t n)
208{
209 u8 bad;
210
211 bad = (s == 0) + (n > smax);
212
213 if (PREDICT_FALSE (bad != 0))
214 {
215 if (s == 0)
216 clib_c11_violation ("s NULL");
217 if (n > smax)
218 clib_c11_violation ("n > smax");
219 return (EINVAL);
220 }
221 memset (s, c, n);
222 return (EOK);
223}
224
225/*
226 * This macro is not [so much of] a crutch.
227 * It's super-typical to write:
228 *
229 * ep = pool_get (<pool>);
230 * clib_memset(ep, 0, sizeof (*ep));
231 *
232 * The compiler should delete the not-so useful
233 * (n > smax) test. TBH the NULL pointer check isn't
234 * so useful in this case, but so be it.
235 */
236#define clib_memset(s,c,n) memset_s_inline(s,n,c,n)
237
Damjan Marion14864772018-05-22 14:07:47 +0200238static_always_inline void
Damjan Marionc59b9a22019-03-19 15:38:40 +0100239clib_memcpy_le (u8 * dst, u8 * src, u8 len, u8 max_len)
240{
Igor Mikhailov (imichail)419e15f2019-05-13 12:04:04 -0700241#if defined (CLIB_HAVE_VEC256)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100242 u8x32 s0, s1, d0, d1;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100243 u8x32 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
244 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
245 };
246 u8x32 lv = u8x32_splat (len);
247 u8x32 add = u8x32_splat (32);
248
Damjan Marion4d3aa072019-03-28 16:19:24 +0100249 s0 = u8x32_load_unaligned (src);
250 s1 = u8x32_load_unaligned (src + 32);
251 d0 = u8x32_load_unaligned (dst);
252 d1 = u8x32_load_unaligned (dst + 32);
253
Damjan Marione01ce5a2022-02-10 15:23:15 +0100254 d0 = u8x32_blend (d0, s0, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100255 u8x32_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100256
257 if (max_len <= 32)
258 return;
259
260 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100261 d1 = u8x32_blend (d1, s1, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100262 u8x32_store_unaligned (d1, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100263
Lijian.Zhang37c83782019-04-04 15:26:26 +0800264#elif defined (CLIB_HAVE_VEC128)
Damjan Marion4d3aa072019-03-28 16:19:24 +0100265 u8x16 s0, s1, s2, s3, d0, d1, d2, d3;
Damjan Marionc59b9a22019-03-19 15:38:40 +0100266 u8x16 mask = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
267 u8x16 lv = u8x16_splat (len);
268 u8x16 add = u8x16_splat (16);
269
Damjan Marion4d3aa072019-03-28 16:19:24 +0100270 s0 = u8x16_load_unaligned (src);
271 s1 = u8x16_load_unaligned (src + 16);
272 s2 = u8x16_load_unaligned (src + 32);
273 s3 = u8x16_load_unaligned (src + 48);
274 d0 = u8x16_load_unaligned (dst);
275 d1 = u8x16_load_unaligned (dst + 16);
276 d2 = u8x16_load_unaligned (dst + 32);
277 d3 = u8x16_load_unaligned (dst + 48);
278
Damjan Marione01ce5a2022-02-10 15:23:15 +0100279 d0 = u8x16_blend (d0, s0, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100280 u8x16_store_unaligned (d0, dst);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100281
282 if (max_len <= 16)
283 return;
284
285 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100286 d1 = u8x16_blend (d1, s1, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100287 u8x16_store_unaligned (d1, dst + 16);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100288
289 if (max_len <= 32)
290 return;
291
292 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100293 d2 = u8x16_blend (d2, s2, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100294 u8x16_store_unaligned (d2, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100295
296 mask += add;
Damjan Marione01ce5a2022-02-10 15:23:15 +0100297 d3 = u8x16_blend (d3, s3, lv > mask);
Damjan Marion4d3aa072019-03-28 16:19:24 +0100298 u8x16_store_unaligned (d3, dst + 48);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100299#else
Damjan Marion4d3aa072019-03-28 16:19:24 +0100300 memmove (dst, src, len);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100301#endif
302}
303
304static_always_inline void
305clib_memcpy_le64 (u8 * dst, u8 * src, u8 len)
306{
307 clib_memcpy_le (dst, src, len, 64);
308}
309
310static_always_inline void
311clib_memcpy_le32 (u8 * dst, u8 * src, u8 len)
312{
313 clib_memcpy_le (dst, src, len, 32);
314}
315
316static_always_inline void
Damjan Marion14864772018-05-22 14:07:47 +0200317clib_memset_u64 (void *p, u64 val, uword count)
318{
319 u64 *ptr = p;
320#if defined(CLIB_HAVE_VEC512)
321 u64x8 v512 = u64x8_splat (val);
322 while (count >= 8)
323 {
324 u64x8_store_unaligned (v512, ptr);
325 ptr += 8;
326 count -= 8;
327 }
328 if (count == 0)
329 return;
330#endif
331#if defined(CLIB_HAVE_VEC256)
332 u64x4 v256 = u64x4_splat (val);
333 while (count >= 4)
334 {
335 u64x4_store_unaligned (v256, ptr);
336 ptr += 4;
337 count -= 4;
338 }
339 if (count == 0)
340 return;
341#else
Damjan Marion137d4ca2022-01-31 17:42:44 +0100342#if defined(CLIB_HAVE_VEC128)
343 u64x2 v = u64x2_splat (val);
344#endif
Damjan Marion14864772018-05-22 14:07:47 +0200345 while (count >= 4)
346 {
Damjan Marion137d4ca2022-01-31 17:42:44 +0100347#if defined(CLIB_HAVE_VEC128)
348 u64x2_store_unaligned (v, ptr);
349 u64x2_store_unaligned (v, ptr + 2);
350#else
Damjan Marion14864772018-05-22 14:07:47 +0200351 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
Damjan Marion137d4ca2022-01-31 17:42:44 +0100352#endif
Damjan Marion14864772018-05-22 14:07:47 +0200353 ptr += 4;
354 count -= 4;
355 }
356#endif
357 while (count--)
358 ptr++[0] = val;
359}
360
361static_always_inline void
362clib_memset_u32 (void *p, u32 val, uword count)
363{
364 u32 *ptr = p;
365#if defined(CLIB_HAVE_VEC512)
366 u32x16 v512 = u32x16_splat (val);
367 while (count >= 16)
368 {
369 u32x16_store_unaligned (v512, ptr);
370 ptr += 16;
371 count -= 16;
372 }
373 if (count == 0)
374 return;
375#endif
376#if defined(CLIB_HAVE_VEC256)
377 u32x8 v256 = u32x8_splat (val);
378 while (count >= 8)
379 {
380 u32x8_store_unaligned (v256, ptr);
381 ptr += 8;
382 count -= 8;
383 }
384 if (count == 0)
385 return;
386#endif
387#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
388 u32x4 v128 = u32x4_splat (val);
389 while (count >= 4)
390 {
391 u32x4_store_unaligned (v128, ptr);
392 ptr += 4;
393 count -= 4;
394 }
395#else
396 while (count >= 4)
397 {
398 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
399 ptr += 4;
400 count -= 4;
401 }
402#endif
403 while (count--)
404 ptr++[0] = val;
405}
406
407static_always_inline void
408clib_memset_u16 (void *p, u16 val, uword count)
409{
410 u16 *ptr = p;
411#if defined(CLIB_HAVE_VEC512)
412 u16x32 v512 = u16x32_splat (val);
413 while (count >= 32)
414 {
415 u16x32_store_unaligned (v512, ptr);
416 ptr += 32;
417 count -= 32;
418 }
419 if (count == 0)
420 return;
421#endif
422#if defined(CLIB_HAVE_VEC256)
423 u16x16 v256 = u16x16_splat (val);
424 while (count >= 16)
425 {
426 u16x16_store_unaligned (v256, ptr);
427 ptr += 16;
428 count -= 16;
429 }
430 if (count == 0)
431 return;
432#endif
433#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
434 u16x8 v128 = u16x8_splat (val);
435 while (count >= 8)
436 {
437 u16x8_store_unaligned (v128, ptr);
438 ptr += 8;
439 count -= 8;
440 }
441#else
442 while (count >= 4)
443 {
444 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
445 ptr += 4;
446 count -= 4;
447 }
448#endif
449 while (count--)
450 ptr++[0] = val;
451}
452
453static_always_inline void
454clib_memset_u8 (void *p, u8 val, uword count)
455{
456 u8 *ptr = p;
457#if defined(CLIB_HAVE_VEC512)
458 u8x64 v512 = u8x64_splat (val);
459 while (count >= 64)
460 {
461 u8x64_store_unaligned (v512, ptr);
462 ptr += 64;
463 count -= 64;
464 }
465 if (count == 0)
466 return;
467#endif
468#if defined(CLIB_HAVE_VEC256)
469 u8x32 v256 = u8x32_splat (val);
470 while (count >= 32)
471 {
472 u8x32_store_unaligned (v256, ptr);
473 ptr += 32;
474 count -= 32;
475 }
476 if (count == 0)
477 return;
478#endif
479#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
480 u8x16 v128 = u8x16_splat (val);
481 while (count >= 16)
482 {
483 u8x16_store_unaligned (v128, ptr);
484 ptr += 16;
485 count -= 16;
486 }
487#else
488 while (count >= 4)
489 {
490 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
491 ptr += 4;
492 count -= 4;
493 }
494#endif
495 while (count--)
496 ptr++[0] = val;
497}
498
Damjan Marion14864772018-05-22 14:07:47 +0200499
Stevenb0598492018-10-24 21:15:45 -0700500/*
501 * This macro is to provide smooth mapping from memcmp to memcmp_s.
502 * memcmp has fewer parameters and fewer returns than memcmp_s.
503 * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
504 * we return 0 and spit out a message in the console because there is
505 * no way to return the error code to the memcmp callers.
506 * This condition happens when s1 or s2 is null. Please note
507 * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
508 * anyway. So we are consistent in this case for the comparison return
509 * although we also spit out a C11 violation message in the console to
510 * warn that they pass null pointers for both s1 and s2.
511 * Applications are encouraged to use the cool C11 memcmp_s API to get the
512 * maximum benefit out of it.
513 */
514#define clib_memcmp(s1,s2,m1) \
515 ({ int __diff = 0; \
516 memcmp_s_inline (s1, m1, s2, m1, &__diff); \
517 __diff; \
518 })
519
520errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
521 rsize_t s2max, int *diff);
522
523always_inline errno_t
524memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
525 int *diff)
526{
527 u8 bad;
528
529 bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
530 (s1max == 0);
531
532 if (PREDICT_FALSE (bad != 0))
533 {
534 if (s1 == NULL)
535 clib_c11_violation ("s1 NULL");
536 if (s2 == NULL)
537 clib_c11_violation ("s2 NULL");
538 if (diff == NULL)
539 clib_c11_violation ("diff NULL");
540 if (s2max > s1max)
541 clib_c11_violation ("s2max > s1max");
542 if (s2max == 0)
543 clib_c11_violation ("s2max 0");
544 if (s1max == 0)
545 clib_c11_violation ("s1max 0");
546 return EINVAL;
547 }
548
549 if (PREDICT_FALSE (s1 == s2))
550 {
551 *diff = 0;
552 return EOK;
553 }
554
555 *diff = memcmp (s1, s2, s2max);
556 return EOK;
557}
558
559/*
560 * This macro is to provide smooth mapping from strnlen to strnlen_s
561 */
562#define clib_strnlen(s,m) strnlen_s_inline(s,m)
563
564size_t strnlen_s (const char *s, size_t maxsize);
565
566always_inline size_t
567strnlen_s_inline (const char *s, size_t maxsize)
568{
569 u8 bad;
570
571 bad = (s == 0) + (maxsize == 0);
572 if (PREDICT_FALSE (bad != 0))
573 {
574 if (s == 0)
575 clib_c11_violation ("s NULL");
576 if (maxsize == 0)
577 clib_c11_violation ("maxsize 0");
578 return 0;
579 }
580 return strnlen (s, maxsize);
581}
582
583/*
584 * This macro is to provide smooth mapping from strcmp to strcmp_s.
585 * strcmp has fewer parameters and fewer returns than strcmp_s.
586 * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
587 * we return 0 and spit out a message in the console because
588 * there is no way to return the error to the strcmp callers.
589 * This condition happens when s1 or s2 is null. Please note in the extant
590 * strcmp call, they would end up crashing if one of them is null.
591 * So the new behavior is no crash, but an error is displayed in the
592 * console which I think is more user friendly. If both s1 and s2 are null,
593 * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
594 * to actually accessing the pointer contents. We are still consistent
595 * in this case for the comparison return although we also spit out a
596 * C11 violation message in the console to warn that they pass null pointers
597 * for both s1 and s2. The other problem is strcmp does not provide s1max,
598 * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
599 * If not, we may be accessing memory beyonf what is intended.
600 * Applications are encouraged to use the cool C11 strcmp_s API to get the
601 * maximum benefit out of it.
602 */
603#define clib_strcmp(s1,s2) \
604 ({ int __indicator = 0; \
605 strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
606 __indicator; \
607 })
608
609errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
610 int *indicator);
611
612always_inline errno_t
613strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
614 int *indicator)
615{
616 u8 bad;
617
618 bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
619 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
620
621 if (PREDICT_FALSE (bad != 0))
622 {
623 if (indicator == NULL)
624 clib_c11_violation ("indicator NULL");
625 if (s1 == NULL)
626 clib_c11_violation ("s1 NULL");
627 if (s2 == NULL)
628 clib_c11_violation ("s2 NULL");
629 if (s1max == 0)
630 clib_c11_violation ("s1max 0");
631 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
632 clib_c11_violation ("s1 unterminated");
633 return EINVAL;
634 }
635
636 *indicator = strcmp (s1, s2);
637 return EOK;
638}
639
640/*
641 * This macro is to provide smooth mapping from strncmp to strncmp_s.
642 * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
643 * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
644 * we return 0 and spit out a message in the console because there is no
645 * means to return the error to the strncmp caller.
646 * This condition happens when s1 or s2 is null. In the extant strncmp call,
647 * they would end up crashing if one of them is null. So the new behavior is
648 * no crash, but error is displayed in the console which is more
649 * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
650 * strncmp did the pointers comparison prior to actually accessing the
651 * pointer contents. We are still consistent in this case for the comparison
652 * return although we also spit out a C11 violation message in the console to
653 * warn that they pass null pointers for both s1 and s2.
654 * Applications are encouraged to use the cool C11 strncmp_s API to get the
655 * maximum benefit out of it.
656 */
657#define clib_strncmp(s1,s2,n) \
658 ({ int __indicator = 0; \
659 strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
660 __indicator; \
661 })
662
663errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
664 int *indicator);
665
666always_inline errno_t
667strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
668 int *indicator)
669{
670 u8 bad;
671 u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
672
673 if (PREDICT_FALSE (s1_greater_s1max && indicator))
674 {
675 /*
676 * strcmp allows n > s1max. If indicator is non null, we can still
677 * do the compare without any harm and return EINVAL as well as the
678 * result in indicator.
679 */
680 clib_c11_violation ("n exceeds s1 length");
681 *indicator = strncmp (s1, s2, n);
682 return EINVAL;
683 }
684
685 bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
686 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
687
688 if (PREDICT_FALSE (bad != 0))
689 {
690 if (indicator == NULL)
691 clib_c11_violation ("indicator NULL");
692 if (s1 == NULL)
693 clib_c11_violation ("s1 NULL");
694 if (s2 == NULL)
695 clib_c11_violation ("s2 NULL");
696 if (s1max == 0)
697 clib_c11_violation ("s1max 0");
698 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
699 clib_c11_violation ("s1 unterminated");
700 if (s1_greater_s1max)
701 clib_c11_violation ("n exceeds s1 length");
702 return EINVAL;
703 }
704
705 *indicator = strncmp (s1, s2, n);
706 return EOK;
707}
708
Stevenb0598492018-10-24 21:15:45 -0700709errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
710 const char *__restrict__ src);
711
712always_inline errno_t
713strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
714 const char *__restrict__ src)
715{
716 u8 bad;
717 uword low, hi;
718 size_t n;
719
720 bad = (dest == 0) + (dmax == 0) + (src == 0);
721 if (PREDICT_FALSE (bad != 0))
722 {
723 if (dest == 0)
724 clib_c11_violation ("dest NULL");
725 if (src == 0)
726 clib_c11_violation ("src NULL");
727 if (dmax == 0)
728 clib_c11_violation ("dmax 0");
729 return EINVAL;
730 }
731
732 n = clib_strnlen (src, dmax);
733 if (PREDICT_FALSE (n >= dmax))
734 {
735 clib_c11_violation ("not enough space for dest");
736 return (EINVAL);
737 }
738 /* Not actually trying to copy anything is OK */
739 if (PREDICT_FALSE (n == 0))
740 return EOK;
741
742 /* Check for src/dst overlap, which is not allowed */
743 low = (uword) (src < dest ? src : dest);
744 hi = (uword) (src < dest ? dest : src);
745
746 if (PREDICT_FALSE (low + (n - 1) >= hi))
747 {
748 clib_c11_violation ("src/dest overlap");
749 return EINVAL;
750 }
751
752 clib_memcpy_fast (dest, src, n);
753 dest[n] = '\0';
754 return EOK;
755}
756
757/*
758 * This macro is provided for smooth migration from strncpy. It is not perfect
759 * because we don't know the size of the destination buffer to pass to
760 * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
761 * Applications are encouraged to move to the C11 strncpy_s API and provide
762 * the correct dmax for better error checking.
763 */
764#define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
765
766errno_t
767strncpy_s (char *__restrict__ dest, rsize_t dmax,
768 const char *__restrict__ src, rsize_t n);
769
770always_inline errno_t
771strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
772 const char *__restrict__ src, rsize_t n)
773{
774 u8 bad;
775 uword low, hi;
776 rsize_t m;
777 errno_t status = EOK;
778
779 bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
780 if (PREDICT_FALSE (bad != 0))
781 {
782 /* Not actually trying to copy anything is OK */
783 if (n == 0)
784 return EOK;
785 if (dest == 0)
786 clib_c11_violation ("dest NULL");
787 if (src == 0)
788 clib_c11_violation ("src NULL");
789 if (dmax == 0)
790 clib_c11_violation ("dmax 0");
791 return EINVAL;
792 }
793
794 if (PREDICT_FALSE (n >= dmax))
795 {
796 /* Relax and use strnlen of src */
797 clib_c11_violation ("n >= dmax");
798 m = clib_strnlen (src, dmax);
799 if (m >= dmax)
800 {
801 /* Truncate, adjust copy length to fit dest */
802 m = dmax - 1;
803 status = EOVERFLOW;
804 }
805 }
806 else
Stevenf09179f2019-01-07 20:32:01 -0800807 /* cap the copy to strlen(src) in case n > strlen(src) */
808 m = clib_strnlen (src, n);
Stevenb0598492018-10-24 21:15:45 -0700809
810 /* Check for src/dst overlap, which is not allowed */
811 low = (uword) (src < dest ? src : dest);
812 hi = (uword) (src < dest ? dest : src);
813
Dave Barachd08ae852018-12-05 08:41:11 -0500814 /*
815 * This check may fail innocently if src + dmax >= dst, but
816 * src + strlen(src) < dst. If it fails, check more carefully before
817 * blowing the whistle.
818 */
Stevenb0598492018-10-24 21:15:45 -0700819 if (PREDICT_FALSE (low + (m - 1) >= hi))
820 {
Dave Barachd08ae852018-12-05 08:41:11 -0500821 m = clib_strnlen (src, m);
822
823 if (low + (m - 1) >= hi)
824 {
825 clib_c11_violation ("src/dest overlap");
826 return EINVAL;
827 }
Stevenb0598492018-10-24 21:15:45 -0700828 }
829
830 clib_memcpy_fast (dest, src, m);
831 dest[m] = '\0';
832 return status;
833}
834
Stevenb0598492018-10-24 21:15:45 -0700835errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
836 const char *__restrict__ src);
837
838always_inline errno_t
839strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
840 const char *__restrict__ src)
841{
842 u8 bad;
843 uword low, hi;
844 size_t m, n, dest_size;
845
846 bad = (dest == 0) + (dmax == 0) + (src == 0);
847 if (PREDICT_FALSE (bad != 0))
848 {
849 if (dest == 0)
850 clib_c11_violation ("dest NULL");
851 if (src == 0)
852 clib_c11_violation ("src NULL");
853 if (dmax == 0)
854 clib_c11_violation ("dmax 0");
855 return EINVAL;
856 }
857
858 dest_size = clib_strnlen (dest, dmax);
859 m = dmax - dest_size;
860 n = clib_strnlen (src, m);
861 if (PREDICT_FALSE (n >= m))
862 {
863 clib_c11_violation ("not enough space for dest");
864 return EINVAL;
865 }
866
867 /* Not actually trying to concatenate anything is OK */
868 if (PREDICT_FALSE (n == 0))
869 return EOK;
870
871 /* Check for src/dst overlap, which is not allowed */
872 low = (uword) (src < dest ? src : dest);
873 hi = (uword) (src < dest ? dest : src);
874
875 if (PREDICT_FALSE (low + (n - 1) >= hi))
876 {
877 clib_c11_violation ("src/dest overlap");
878 return EINVAL;
879 }
880
881 clib_memcpy_fast (dest + dest_size, src, n);
882 dest[dest_size + n] = '\0';
883 return EOK;
884}
885
Stevenb0598492018-10-24 21:15:45 -0700886errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
887 const char *__restrict__ src, rsize_t n);
888
889always_inline errno_t
890strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
891 const char *__restrict__ src, rsize_t n)
892{
893 u8 bad;
894 uword low, hi;
895 size_t m, dest_size, allowed_size;
896 errno_t status = EOK;
897
898 bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
899 if (PREDICT_FALSE (bad != 0))
900 {
901 /* Not actually trying to concatenate anything is OK */
902 if (n == 0)
903 return EOK;
904 if (dest == 0)
905 clib_c11_violation ("dest NULL");
906 if (src == 0)
907 clib_c11_violation ("src NULL");
908 if (dmax == 0)
909 clib_c11_violation ("dmax 0");
910 return EINVAL;
911 }
912
913 /* Check for src/dst overlap, which is not allowed */
914 low = (uword) (src < dest ? src : dest);
915 hi = (uword) (src < dest ? dest : src);
916
917 if (PREDICT_FALSE (low + (n - 1) >= hi))
918 {
919 clib_c11_violation ("src/dest overlap");
920 return EINVAL;
921 }
922
923 dest_size = clib_strnlen (dest, dmax);
924 allowed_size = dmax - dest_size;
925
926 if (PREDICT_FALSE (allowed_size == 0))
927 {
928 clib_c11_violation ("no space left in dest");
929 return (EINVAL);
930 }
931
932 if (PREDICT_FALSE (n >= allowed_size))
933 {
934 /*
935 * unlike strcat_s, strncat_s will do the concatenation anyway when
936 * there is not enough space in dest. But it will do the truncation and
937 * null terminate dest
938 */
939 m = clib_strnlen (src, allowed_size);
940 if (m >= allowed_size)
941 {
942 m = allowed_size - 1;
943 status = EOVERFLOW;
944 }
945 }
946 else
947 m = clib_strnlen (src, n);
948
949 clib_memcpy_fast (dest + dest_size, src, m);
950 dest[dest_size + m] = '\0';
951 return status;
952}
953
954/*
955 * This macro is to provide smooth mapping from strtok_r to strtok_s.
956 * To map strtok to this macro, the caller would have to supply an additional
957 * argument. strtokr_s requires s1max which the unsafe API does not have. So
958 * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
959 * this macro cannot catch unterminated s1 and s2.
960 * Applications are encouraged to use the cool C11 strtok_s API to avoid
961 * these problems.
962 */
963#define clib_strtok(s1,s2,p) \
964 ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
965 strtok_s_inline (s1, &__s1max, s2, p); \
966 })
967
968char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
969 const char *__restrict__ s2, char **__restrict__ ptr);
970
971always_inline char *
972strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
973 const char *__restrict__ s2, char **__restrict__ ptr)
974{
975#define STRTOK_DELIM_MAX_LEN 16
976 u8 bad;
977 const char *pt;
978 char *ptoken;
979 uword dlen, slen;
980
981 bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
982 ((s1 == 0) && ptr && (*ptr == 0));
983 if (PREDICT_FALSE (bad != 0))
984 {
985 if (s2 == NULL)
986 clib_c11_violation ("s2 NULL");
987 if (s1max == NULL)
988 clib_c11_violation ("s1max is NULL");
989 if (ptr == NULL)
990 clib_c11_violation ("ptr is NULL");
991 /* s1 == 0 and *ptr == null is no good */
992 if ((s1 == 0) && ptr && (*ptr == 0))
993 clib_c11_violation ("s1 and ptr contents are NULL");
994 return 0;
995 }
996
997 if (s1 == 0)
998 s1 = *ptr;
999
1000 /*
1001 * scan s1 for a delimiter
1002 */
1003 dlen = *s1max;
1004 ptoken = 0;
1005 while (*s1 != '\0' && !ptoken)
1006 {
1007 if (PREDICT_FALSE (dlen == 0))
1008 {
1009 *ptr = 0;
1010 clib_c11_violation ("s1 unterminated");
1011 return 0;
1012 }
1013
1014 /*
1015 * must scan the entire delimiter list
1016 * ISO should have included a delimiter string limit!!
1017 */
1018 slen = STRTOK_DELIM_MAX_LEN;
1019 pt = s2;
1020 while (*pt != '\0')
1021 {
1022 if (PREDICT_FALSE (slen == 0))
1023 {
1024 *ptr = 0;
1025 clib_c11_violation ("s2 unterminated");
1026 return 0;
1027 }
1028 slen--;
1029 if (*s1 == *pt)
1030 {
1031 ptoken = 0;
1032 break;
1033 }
1034 else
1035 {
1036 pt++;
1037 ptoken = s1;
1038 }
1039 }
1040 s1++;
1041 dlen--;
1042 }
1043
1044 /*
1045 * if the beginning of a token was not found, then no
1046 * need to continue the scan.
1047 */
1048 if (ptoken == 0)
1049 {
1050 *s1max = dlen;
1051 return (ptoken);
1052 }
1053
1054 /*
1055 * Now we need to locate the end of the token
1056 */
1057 while (*s1 != '\0')
1058 {
1059 if (dlen == 0)
1060 {
1061 *ptr = 0;
1062 clib_c11_violation ("s1 unterminated");
1063 return 0;
1064 }
1065
1066 slen = STRTOK_DELIM_MAX_LEN;
1067 pt = s2;
1068 while (*pt != '\0')
1069 {
1070 if (slen == 0)
1071 {
1072 *ptr = 0;
1073 clib_c11_violation ("s2 unterminated");
1074 return 0;
1075 }
1076 slen--;
1077 if (*s1 == *pt)
1078 {
1079 /*
1080 * found a delimiter, set to null
1081 * and return context ptr to next char
1082 */
1083 *s1 = '\0';
1084 *ptr = (s1 + 1); /* return pointer for next scan */
1085 *s1max = dlen - 1; /* account for the nulled delimiter */
1086 return (ptoken);
1087 }
1088 else
1089 {
1090 /*
1091 * simply scanning through the delimiter string
1092 */
1093 pt++;
1094 }
1095 }
1096 s1++;
1097 dlen--;
1098 }
1099
1100 *ptr = s1;
1101 *s1max = dlen;
1102 return (ptoken);
1103}
1104
Stevenb0598492018-10-24 21:15:45 -07001105errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1106 char **substring);
1107
1108always_inline errno_t
1109strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1110 char **substring)
1111{
1112 u8 bad;
1113 size_t s1_size, s2_size;
1114
1115 bad =
1116 (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
1117 (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
1118 (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
1119 if (PREDICT_FALSE (bad != 0))
1120 {
1121 if (s1 == 0)
1122 clib_c11_violation ("s1 NULL");
1123 if (s2 == 0)
1124 clib_c11_violation ("s2 NULL");
1125 if (s1max == 0)
1126 clib_c11_violation ("s1max 0");
1127 if (s2max == 0)
1128 clib_c11_violation ("s2max 0");
1129 if (substring == 0)
1130 clib_c11_violation ("substring NULL");
1131 if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
1132 clib_c11_violation ("s1 unterminated");
Steven Luongb5a2b052021-11-03 16:49:04 -07001133 if (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'))
Stevenb0598492018-10-24 21:15:45 -07001134 clib_c11_violation ("s2 unterminated");
1135 return EINVAL;
1136 }
1137
1138 /*
1139 * s2 points to a string with zero length, or s2 equals s1, return s1
1140 */
1141 if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1142 {
1143 *substring = s1;
1144 return EOK;
1145 }
1146
1147 /*
1148 * s2_size > s1_size, it won't find match.
1149 */
1150 s1_size = clib_strnlen (s1, s1max);
1151 s2_size = clib_strnlen (s2, s2max);
1152 if (PREDICT_FALSE (s2_size > s1_size))
1153 return ESRCH;
1154
1155 *substring = strstr (s1, s2);
1156 if (*substring == 0)
1157 return ESRCH;
1158
1159 return EOK;
1160}
1161
Ed Warnickecb9cada2015-12-08 15:45:58 -07001162#endif /* included_clib_string_h */
Dave Barachc3799992016-08-15 11:12:27 -04001163
1164/*
1165 * fd.io coding-style-patch-verification: ON
1166 *
1167 * Local Variables:
1168 * eval: (c-set-style "gnu")
1169 * End:
1170 */