blob: 758a541814d7669d7af5ec25ca38f69572012529 [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
235 d0 = u8x32_blend (d0, s0, u8x32_is_greater (lv, mask));
236 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 Marion4d3aa072019-03-28 16:19:24 +0100242 d1 = u8x32_blend (d1, s1, u8x32_is_greater (lv, mask));
243 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
260 d0 = u8x16_blend (d0, s0, u8x16_is_greater (lv, mask));
261 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 Marion4d3aa072019-03-28 16:19:24 +0100267 d1 = u8x16_blend (d1, s1, u8x16_is_greater (lv, mask));
268 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 Marion4d3aa072019-03-28 16:19:24 +0100274 d2 = u8x16_blend (d2, s2, u8x16_is_greater (lv, mask));
275 u8x16_store_unaligned (d2, dst + 32);
Damjan Marionc59b9a22019-03-19 15:38:40 +0100276
277 mask += add;
Damjan Marion4d3aa072019-03-28 16:19:24 +0100278 d3 = u8x16_blend (d3, s3, u8x16_is_greater (lv, mask));
279 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
323 while (count >= 4)
324 {
325 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
326 ptr += 4;
327 count -= 4;
328 }
329#endif
330 while (count--)
331 ptr++[0] = val;
332}
333
334static_always_inline void
335clib_memset_u32 (void *p, u32 val, uword count)
336{
337 u32 *ptr = p;
338#if defined(CLIB_HAVE_VEC512)
339 u32x16 v512 = u32x16_splat (val);
340 while (count >= 16)
341 {
342 u32x16_store_unaligned (v512, ptr);
343 ptr += 16;
344 count -= 16;
345 }
346 if (count == 0)
347 return;
348#endif
349#if defined(CLIB_HAVE_VEC256)
350 u32x8 v256 = u32x8_splat (val);
351 while (count >= 8)
352 {
353 u32x8_store_unaligned (v256, ptr);
354 ptr += 8;
355 count -= 8;
356 }
357 if (count == 0)
358 return;
359#endif
360#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
361 u32x4 v128 = u32x4_splat (val);
362 while (count >= 4)
363 {
364 u32x4_store_unaligned (v128, ptr);
365 ptr += 4;
366 count -= 4;
367 }
368#else
369 while (count >= 4)
370 {
371 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
372 ptr += 4;
373 count -= 4;
374 }
375#endif
376 while (count--)
377 ptr++[0] = val;
378}
379
380static_always_inline void
381clib_memset_u16 (void *p, u16 val, uword count)
382{
383 u16 *ptr = p;
384#if defined(CLIB_HAVE_VEC512)
385 u16x32 v512 = u16x32_splat (val);
386 while (count >= 32)
387 {
388 u16x32_store_unaligned (v512, ptr);
389 ptr += 32;
390 count -= 32;
391 }
392 if (count == 0)
393 return;
394#endif
395#if defined(CLIB_HAVE_VEC256)
396 u16x16 v256 = u16x16_splat (val);
397 while (count >= 16)
398 {
399 u16x16_store_unaligned (v256, ptr);
400 ptr += 16;
401 count -= 16;
402 }
403 if (count == 0)
404 return;
405#endif
406#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
407 u16x8 v128 = u16x8_splat (val);
408 while (count >= 8)
409 {
410 u16x8_store_unaligned (v128, ptr);
411 ptr += 8;
412 count -= 8;
413 }
414#else
415 while (count >= 4)
416 {
417 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
418 ptr += 4;
419 count -= 4;
420 }
421#endif
422 while (count--)
423 ptr++[0] = val;
424}
425
426static_always_inline void
427clib_memset_u8 (void *p, u8 val, uword count)
428{
429 u8 *ptr = p;
430#if defined(CLIB_HAVE_VEC512)
431 u8x64 v512 = u8x64_splat (val);
432 while (count >= 64)
433 {
434 u8x64_store_unaligned (v512, ptr);
435 ptr += 64;
436 count -= 64;
437 }
438 if (count == 0)
439 return;
440#endif
441#if defined(CLIB_HAVE_VEC256)
442 u8x32 v256 = u8x32_splat (val);
443 while (count >= 32)
444 {
445 u8x32_store_unaligned (v256, ptr);
446 ptr += 32;
447 count -= 32;
448 }
449 if (count == 0)
450 return;
451#endif
452#if defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
453 u8x16 v128 = u8x16_splat (val);
454 while (count >= 16)
455 {
456 u8x16_store_unaligned (v128, ptr);
457 ptr += 16;
458 count -= 16;
459 }
460#else
461 while (count >= 4)
462 {
463 ptr[0] = ptr[1] = ptr[2] = ptr[3] = val;
464 ptr += 4;
465 count -= 4;
466 }
467#endif
468 while (count--)
469 ptr++[0] = val;
470}
471
Damjan Marion14864772018-05-22 14:07:47 +0200472
Stevenb0598492018-10-24 21:15:45 -0700473/*
474 * This macro is to provide smooth mapping from memcmp to memcmp_s.
475 * memcmp has fewer parameters and fewer returns than memcmp_s.
476 * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
477 * we return 0 and spit out a message in the console because there is
478 * no way to return the error code to the memcmp callers.
479 * This condition happens when s1 or s2 is null. Please note
480 * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
481 * anyway. So we are consistent in this case for the comparison return
482 * although we also spit out a C11 violation message in the console to
483 * warn that they pass null pointers for both s1 and s2.
484 * Applications are encouraged to use the cool C11 memcmp_s API to get the
485 * maximum benefit out of it.
486 */
487#define clib_memcmp(s1,s2,m1) \
488 ({ int __diff = 0; \
489 memcmp_s_inline (s1, m1, s2, m1, &__diff); \
490 __diff; \
491 })
492
493errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
494 rsize_t s2max, int *diff);
495
496always_inline errno_t
497memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
498 int *diff)
499{
500 u8 bad;
501
502 bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
503 (s1max == 0);
504
505 if (PREDICT_FALSE (bad != 0))
506 {
507 if (s1 == NULL)
508 clib_c11_violation ("s1 NULL");
509 if (s2 == NULL)
510 clib_c11_violation ("s2 NULL");
511 if (diff == NULL)
512 clib_c11_violation ("diff NULL");
513 if (s2max > s1max)
514 clib_c11_violation ("s2max > s1max");
515 if (s2max == 0)
516 clib_c11_violation ("s2max 0");
517 if (s1max == 0)
518 clib_c11_violation ("s1max 0");
519 return EINVAL;
520 }
521
522 if (PREDICT_FALSE (s1 == s2))
523 {
524 *diff = 0;
525 return EOK;
526 }
527
528 *diff = memcmp (s1, s2, s2max);
529 return EOK;
530}
531
532/*
533 * This macro is to provide smooth mapping from strnlen to strnlen_s
534 */
535#define clib_strnlen(s,m) strnlen_s_inline(s,m)
536
537size_t strnlen_s (const char *s, size_t maxsize);
538
539always_inline size_t
540strnlen_s_inline (const char *s, size_t maxsize)
541{
542 u8 bad;
543
544 bad = (s == 0) + (maxsize == 0);
545 if (PREDICT_FALSE (bad != 0))
546 {
547 if (s == 0)
548 clib_c11_violation ("s NULL");
549 if (maxsize == 0)
550 clib_c11_violation ("maxsize 0");
551 return 0;
552 }
553 return strnlen (s, maxsize);
554}
555
556/*
557 * This macro is to provide smooth mapping from strcmp to strcmp_s.
558 * strcmp has fewer parameters and fewer returns than strcmp_s.
559 * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
560 * we return 0 and spit out a message in the console because
561 * there is no way to return the error to the strcmp callers.
562 * This condition happens when s1 or s2 is null. Please note in the extant
563 * strcmp call, they would end up crashing if one of them is null.
564 * So the new behavior is no crash, but an error is displayed in the
565 * console which I think is more user friendly. If both s1 and s2 are null,
566 * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
567 * to actually accessing the pointer contents. We are still consistent
568 * in this case for the comparison return although we also spit out a
569 * C11 violation message in the console to warn that they pass null pointers
570 * for both s1 and s2. The other problem is strcmp does not provide s1max,
571 * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
572 * If not, we may be accessing memory beyonf what is intended.
573 * Applications are encouraged to use the cool C11 strcmp_s API to get the
574 * maximum benefit out of it.
575 */
576#define clib_strcmp(s1,s2) \
577 ({ int __indicator = 0; \
578 strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
579 __indicator; \
580 })
581
582errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
583 int *indicator);
584
585always_inline errno_t
586strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
587 int *indicator)
588{
589 u8 bad;
590
591 bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
592 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
593
594 if (PREDICT_FALSE (bad != 0))
595 {
596 if (indicator == NULL)
597 clib_c11_violation ("indicator NULL");
598 if (s1 == NULL)
599 clib_c11_violation ("s1 NULL");
600 if (s2 == NULL)
601 clib_c11_violation ("s2 NULL");
602 if (s1max == 0)
603 clib_c11_violation ("s1max 0");
604 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
605 clib_c11_violation ("s1 unterminated");
606 return EINVAL;
607 }
608
609 *indicator = strcmp (s1, s2);
610 return EOK;
611}
612
613/*
614 * This macro is to provide smooth mapping from strncmp to strncmp_s.
615 * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
616 * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
617 * we return 0 and spit out a message in the console because there is no
618 * means to return the error to the strncmp caller.
619 * This condition happens when s1 or s2 is null. In the extant strncmp call,
620 * they would end up crashing if one of them is null. So the new behavior is
621 * no crash, but error is displayed in the console which is more
622 * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
623 * strncmp did the pointers comparison prior to actually accessing the
624 * pointer contents. We are still consistent in this case for the comparison
625 * return although we also spit out a C11 violation message in the console to
626 * warn that they pass null pointers for both s1 and s2.
627 * Applications are encouraged to use the cool C11 strncmp_s API to get the
628 * maximum benefit out of it.
629 */
630#define clib_strncmp(s1,s2,n) \
631 ({ int __indicator = 0; \
632 strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
633 __indicator; \
634 })
635
636errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
637 int *indicator);
638
639always_inline errno_t
640strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
641 int *indicator)
642{
643 u8 bad;
644 u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
645
646 if (PREDICT_FALSE (s1_greater_s1max && indicator))
647 {
648 /*
649 * strcmp allows n > s1max. If indicator is non null, we can still
650 * do the compare without any harm and return EINVAL as well as the
651 * result in indicator.
652 */
653 clib_c11_violation ("n exceeds s1 length");
654 *indicator = strncmp (s1, s2, n);
655 return EINVAL;
656 }
657
658 bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
659 (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
660
661 if (PREDICT_FALSE (bad != 0))
662 {
663 if (indicator == NULL)
664 clib_c11_violation ("indicator NULL");
665 if (s1 == NULL)
666 clib_c11_violation ("s1 NULL");
667 if (s2 == NULL)
668 clib_c11_violation ("s2 NULL");
669 if (s1max == 0)
670 clib_c11_violation ("s1max 0");
671 if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
672 clib_c11_violation ("s1 unterminated");
673 if (s1_greater_s1max)
674 clib_c11_violation ("n exceeds s1 length");
675 return EINVAL;
676 }
677
678 *indicator = strncmp (s1, s2, n);
679 return EOK;
680}
681
Stevenb0598492018-10-24 21:15:45 -0700682errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
683 const char *__restrict__ src);
684
685always_inline errno_t
686strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
687 const char *__restrict__ src)
688{
689 u8 bad;
690 uword low, hi;
691 size_t n;
692
693 bad = (dest == 0) + (dmax == 0) + (src == 0);
694 if (PREDICT_FALSE (bad != 0))
695 {
696 if (dest == 0)
697 clib_c11_violation ("dest NULL");
698 if (src == 0)
699 clib_c11_violation ("src NULL");
700 if (dmax == 0)
701 clib_c11_violation ("dmax 0");
702 return EINVAL;
703 }
704
705 n = clib_strnlen (src, dmax);
706 if (PREDICT_FALSE (n >= dmax))
707 {
708 clib_c11_violation ("not enough space for dest");
709 return (EINVAL);
710 }
711 /* Not actually trying to copy anything is OK */
712 if (PREDICT_FALSE (n == 0))
713 return EOK;
714
715 /* Check for src/dst overlap, which is not allowed */
716 low = (uword) (src < dest ? src : dest);
717 hi = (uword) (src < dest ? dest : src);
718
719 if (PREDICT_FALSE (low + (n - 1) >= hi))
720 {
721 clib_c11_violation ("src/dest overlap");
722 return EINVAL;
723 }
724
725 clib_memcpy_fast (dest, src, n);
726 dest[n] = '\0';
727 return EOK;
728}
729
730/*
731 * This macro is provided for smooth migration from strncpy. It is not perfect
732 * because we don't know the size of the destination buffer to pass to
733 * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
734 * Applications are encouraged to move to the C11 strncpy_s API and provide
735 * the correct dmax for better error checking.
736 */
737#define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
738
739errno_t
740strncpy_s (char *__restrict__ dest, rsize_t dmax,
741 const char *__restrict__ src, rsize_t n);
742
743always_inline errno_t
744strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
745 const char *__restrict__ src, rsize_t n)
746{
747 u8 bad;
748 uword low, hi;
749 rsize_t m;
750 errno_t status = EOK;
751
752 bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
753 if (PREDICT_FALSE (bad != 0))
754 {
755 /* Not actually trying to copy anything is OK */
756 if (n == 0)
757 return EOK;
758 if (dest == 0)
759 clib_c11_violation ("dest NULL");
760 if (src == 0)
761 clib_c11_violation ("src NULL");
762 if (dmax == 0)
763 clib_c11_violation ("dmax 0");
764 return EINVAL;
765 }
766
767 if (PREDICT_FALSE (n >= dmax))
768 {
769 /* Relax and use strnlen of src */
770 clib_c11_violation ("n >= dmax");
771 m = clib_strnlen (src, dmax);
772 if (m >= dmax)
773 {
774 /* Truncate, adjust copy length to fit dest */
775 m = dmax - 1;
776 status = EOVERFLOW;
777 }
778 }
779 else
Stevenf09179f2019-01-07 20:32:01 -0800780 /* cap the copy to strlen(src) in case n > strlen(src) */
781 m = clib_strnlen (src, n);
Stevenb0598492018-10-24 21:15:45 -0700782
783 /* Check for src/dst overlap, which is not allowed */
784 low = (uword) (src < dest ? src : dest);
785 hi = (uword) (src < dest ? dest : src);
786
Dave Barachd08ae852018-12-05 08:41:11 -0500787 /*
788 * This check may fail innocently if src + dmax >= dst, but
789 * src + strlen(src) < dst. If it fails, check more carefully before
790 * blowing the whistle.
791 */
Stevenb0598492018-10-24 21:15:45 -0700792 if (PREDICT_FALSE (low + (m - 1) >= hi))
793 {
Dave Barachd08ae852018-12-05 08:41:11 -0500794 m = clib_strnlen (src, m);
795
796 if (low + (m - 1) >= hi)
797 {
798 clib_c11_violation ("src/dest overlap");
799 return EINVAL;
800 }
Stevenb0598492018-10-24 21:15:45 -0700801 }
802
803 clib_memcpy_fast (dest, src, m);
804 dest[m] = '\0';
805 return status;
806}
807
Stevenb0598492018-10-24 21:15:45 -0700808errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
809 const char *__restrict__ src);
810
811always_inline errno_t
812strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
813 const char *__restrict__ src)
814{
815 u8 bad;
816 uword low, hi;
817 size_t m, n, dest_size;
818
819 bad = (dest == 0) + (dmax == 0) + (src == 0);
820 if (PREDICT_FALSE (bad != 0))
821 {
822 if (dest == 0)
823 clib_c11_violation ("dest NULL");
824 if (src == 0)
825 clib_c11_violation ("src NULL");
826 if (dmax == 0)
827 clib_c11_violation ("dmax 0");
828 return EINVAL;
829 }
830
831 dest_size = clib_strnlen (dest, dmax);
832 m = dmax - dest_size;
833 n = clib_strnlen (src, m);
834 if (PREDICT_FALSE (n >= m))
835 {
836 clib_c11_violation ("not enough space for dest");
837 return EINVAL;
838 }
839
840 /* Not actually trying to concatenate anything is OK */
841 if (PREDICT_FALSE (n == 0))
842 return EOK;
843
844 /* Check for src/dst overlap, which is not allowed */
845 low = (uword) (src < dest ? src : dest);
846 hi = (uword) (src < dest ? dest : src);
847
848 if (PREDICT_FALSE (low + (n - 1) >= hi))
849 {
850 clib_c11_violation ("src/dest overlap");
851 return EINVAL;
852 }
853
854 clib_memcpy_fast (dest + dest_size, src, n);
855 dest[dest_size + n] = '\0';
856 return EOK;
857}
858
Stevenb0598492018-10-24 21:15:45 -0700859errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
860 const char *__restrict__ src, rsize_t n);
861
862always_inline errno_t
863strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
864 const char *__restrict__ src, rsize_t n)
865{
866 u8 bad;
867 uword low, hi;
868 size_t m, dest_size, allowed_size;
869 errno_t status = EOK;
870
871 bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
872 if (PREDICT_FALSE (bad != 0))
873 {
874 /* Not actually trying to concatenate anything is OK */
875 if (n == 0)
876 return EOK;
877 if (dest == 0)
878 clib_c11_violation ("dest NULL");
879 if (src == 0)
880 clib_c11_violation ("src NULL");
881 if (dmax == 0)
882 clib_c11_violation ("dmax 0");
883 return EINVAL;
884 }
885
886 /* Check for src/dst overlap, which is not allowed */
887 low = (uword) (src < dest ? src : dest);
888 hi = (uword) (src < dest ? dest : src);
889
890 if (PREDICT_FALSE (low + (n - 1) >= hi))
891 {
892 clib_c11_violation ("src/dest overlap");
893 return EINVAL;
894 }
895
896 dest_size = clib_strnlen (dest, dmax);
897 allowed_size = dmax - dest_size;
898
899 if (PREDICT_FALSE (allowed_size == 0))
900 {
901 clib_c11_violation ("no space left in dest");
902 return (EINVAL);
903 }
904
905 if (PREDICT_FALSE (n >= allowed_size))
906 {
907 /*
908 * unlike strcat_s, strncat_s will do the concatenation anyway when
909 * there is not enough space in dest. But it will do the truncation and
910 * null terminate dest
911 */
912 m = clib_strnlen (src, allowed_size);
913 if (m >= allowed_size)
914 {
915 m = allowed_size - 1;
916 status = EOVERFLOW;
917 }
918 }
919 else
920 m = clib_strnlen (src, n);
921
922 clib_memcpy_fast (dest + dest_size, src, m);
923 dest[dest_size + m] = '\0';
924 return status;
925}
926
927/*
928 * This macro is to provide smooth mapping from strtok_r to strtok_s.
929 * To map strtok to this macro, the caller would have to supply an additional
930 * argument. strtokr_s requires s1max which the unsafe API does not have. So
931 * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
932 * this macro cannot catch unterminated s1 and s2.
933 * Applications are encouraged to use the cool C11 strtok_s API to avoid
934 * these problems.
935 */
936#define clib_strtok(s1,s2,p) \
937 ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
938 strtok_s_inline (s1, &__s1max, s2, p); \
939 })
940
941char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
942 const char *__restrict__ s2, char **__restrict__ ptr);
943
944always_inline char *
945strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
946 const char *__restrict__ s2, char **__restrict__ ptr)
947{
948#define STRTOK_DELIM_MAX_LEN 16
949 u8 bad;
950 const char *pt;
951 char *ptoken;
952 uword dlen, slen;
953
954 bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
955 ((s1 == 0) && ptr && (*ptr == 0));
956 if (PREDICT_FALSE (bad != 0))
957 {
958 if (s2 == NULL)
959 clib_c11_violation ("s2 NULL");
960 if (s1max == NULL)
961 clib_c11_violation ("s1max is NULL");
962 if (ptr == NULL)
963 clib_c11_violation ("ptr is NULL");
964 /* s1 == 0 and *ptr == null is no good */
965 if ((s1 == 0) && ptr && (*ptr == 0))
966 clib_c11_violation ("s1 and ptr contents are NULL");
967 return 0;
968 }
969
970 if (s1 == 0)
971 s1 = *ptr;
972
973 /*
974 * scan s1 for a delimiter
975 */
976 dlen = *s1max;
977 ptoken = 0;
978 while (*s1 != '\0' && !ptoken)
979 {
980 if (PREDICT_FALSE (dlen == 0))
981 {
982 *ptr = 0;
983 clib_c11_violation ("s1 unterminated");
984 return 0;
985 }
986
987 /*
988 * must scan the entire delimiter list
989 * ISO should have included a delimiter string limit!!
990 */
991 slen = STRTOK_DELIM_MAX_LEN;
992 pt = s2;
993 while (*pt != '\0')
994 {
995 if (PREDICT_FALSE (slen == 0))
996 {
997 *ptr = 0;
998 clib_c11_violation ("s2 unterminated");
999 return 0;
1000 }
1001 slen--;
1002 if (*s1 == *pt)
1003 {
1004 ptoken = 0;
1005 break;
1006 }
1007 else
1008 {
1009 pt++;
1010 ptoken = s1;
1011 }
1012 }
1013 s1++;
1014 dlen--;
1015 }
1016
1017 /*
1018 * if the beginning of a token was not found, then no
1019 * need to continue the scan.
1020 */
1021 if (ptoken == 0)
1022 {
1023 *s1max = dlen;
1024 return (ptoken);
1025 }
1026
1027 /*
1028 * Now we need to locate the end of the token
1029 */
1030 while (*s1 != '\0')
1031 {
1032 if (dlen == 0)
1033 {
1034 *ptr = 0;
1035 clib_c11_violation ("s1 unterminated");
1036 return 0;
1037 }
1038
1039 slen = STRTOK_DELIM_MAX_LEN;
1040 pt = s2;
1041 while (*pt != '\0')
1042 {
1043 if (slen == 0)
1044 {
1045 *ptr = 0;
1046 clib_c11_violation ("s2 unterminated");
1047 return 0;
1048 }
1049 slen--;
1050 if (*s1 == *pt)
1051 {
1052 /*
1053 * found a delimiter, set to null
1054 * and return context ptr to next char
1055 */
1056 *s1 = '\0';
1057 *ptr = (s1 + 1); /* return pointer for next scan */
1058 *s1max = dlen - 1; /* account for the nulled delimiter */
1059 return (ptoken);
1060 }
1061 else
1062 {
1063 /*
1064 * simply scanning through the delimiter string
1065 */
1066 pt++;
1067 }
1068 }
1069 s1++;
1070 dlen--;
1071 }
1072
1073 *ptr = s1;
1074 *s1max = dlen;
1075 return (ptoken);
1076}
1077
Stevenb0598492018-10-24 21:15:45 -07001078errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1079 char **substring);
1080
1081always_inline errno_t
1082strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
1083 char **substring)
1084{
1085 u8 bad;
1086 size_t s1_size, s2_size;
1087
1088 bad =
1089 (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
1090 (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
1091 (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
1092 if (PREDICT_FALSE (bad != 0))
1093 {
1094 if (s1 == 0)
1095 clib_c11_violation ("s1 NULL");
1096 if (s2 == 0)
1097 clib_c11_violation ("s2 NULL");
1098 if (s1max == 0)
1099 clib_c11_violation ("s1max 0");
1100 if (s2max == 0)
1101 clib_c11_violation ("s2max 0");
1102 if (substring == 0)
1103 clib_c11_violation ("substring NULL");
1104 if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
1105 clib_c11_violation ("s1 unterminated");
Steven Luongb5a2b052021-11-03 16:49:04 -07001106 if (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'))
Stevenb0598492018-10-24 21:15:45 -07001107 clib_c11_violation ("s2 unterminated");
1108 return EINVAL;
1109 }
1110
1111 /*
1112 * s2 points to a string with zero length, or s2 equals s1, return s1
1113 */
1114 if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
1115 {
1116 *substring = s1;
1117 return EOK;
1118 }
1119
1120 /*
1121 * s2_size > s1_size, it won't find match.
1122 */
1123 s1_size = clib_strnlen (s1, s1max);
1124 s2_size = clib_strnlen (s2, s2max);
1125 if (PREDICT_FALSE (s2_size > s1_size))
1126 return ESRCH;
1127
1128 *substring = strstr (s1, s2);
1129 if (*substring == 0)
1130 return ESRCH;
1131
1132 return EOK;
1133}
1134
Ed Warnickecb9cada2015-12-08 15:45:58 -07001135#endif /* included_clib_string_h */
Dave Barachc3799992016-08-15 11:12:27 -04001136
1137/*
1138 * fd.io coding-style-patch-verification: ON
1139 *
1140 * Local Variables:
1141 * eval: (c-set-style "gnu")
1142 * End:
1143 */