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