1 /*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/std.h"
10
11 #include "hf/check.h"
12
13 /* Declare unsafe functions locally so they are not available globally. */
14 void *memset(void *s, int c, size_t n);
15 void *memcpy(void *dst, const void *src, size_t n);
16 void *memmove(void *dst, const void *src, size_t n);
17
18 /*
19 * As per the C11 specification, mem*_s() operations fill the destination buffer
20 * if runtime constraint validation fails, assuming that `dest` and `destsz`
21 * are both valid.
22 */
23 #define CHECK_OR_FILL(cond, dest, destsz, ch) \
24 do { \
25 if (!(cond)) { \
26 if ((dest) != NULL && (destsz) <= RSIZE_MAX) { \
27 memset_s((dest), (destsz), (ch), (destsz)); \
28 } \
29 panic("%s failed: " #cond, __func__); \
30 } \
31 } while (0)
32
33 #define CHECK_OR_ZERO_FILL(cond, dest, destsz) \
34 CHECK_OR_FILL(cond, dest, destsz, '\0')
35
memset_s(void * dest,rsize_t destsz,int ch,rsize_t count)36 void memset_s(void *dest, rsize_t destsz, int ch, rsize_t count)
37 {
38 if (dest == NULL || destsz > RSIZE_MAX) {
39 panic("memset_s failed as either dest == NULL "
40 "or destsz > RSIZE_MAX.\n");
41 }
42
43 /*
44 * Clang analyzer doesn't like us calling unsafe memory functions, so
45 * make it ignore this call.
46 */
47 // NOLINTNEXTLINE
48 memset(dest, ch, (count <= destsz ? count : destsz));
49 }
50
memcpy_s(void * dest,rsize_t destsz,const void * src,rsize_t count)51 void memcpy_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
52 {
53 uintptr_t d = (uintptr_t)dest;
54 uintptr_t s = (uintptr_t)src;
55
56 CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
57 CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
58
59 /* Check count <= destsz <= RSIZE_MAX. */
60 CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
61 CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
62
63 /*
64 * Buffer overlap test.
65 * case a) `d < s` implies `s >= d+count`
66 * case b) `d > s` implies `d >= s+count`
67 */
68 CHECK_OR_ZERO_FILL(d != s, dest, destsz);
69 CHECK_OR_ZERO_FILL(d < s || d >= (s + count), dest, destsz);
70 CHECK_OR_ZERO_FILL(d > s || s >= (d + count), dest, destsz);
71
72 /*
73 * Clang analyzer doesn't like us calling unsafe memory functions, so
74 * make it ignore this call.
75 */
76 // NOLINTNEXTLINE
77 memcpy(dest, src, count);
78 }
79
memmove_s(void * dest,rsize_t destsz,const void * src,rsize_t count)80 void memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
81 {
82 CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
83 CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
84
85 /* Check count <= destsz <= RSIZE_MAX. */
86 CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
87 CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
88
89 /*
90 * Clang analyzer doesn't like us calling unsafe memory functions, so
91 * make it ignore this call.
92 */
93 // NOLINTNEXTLINE
94 memmove(dest, src, count);
95 }
96
97 /**
98 * Finds the first occurrence of character `ch` in the first `count` bytes of
99 * memory pointed to by `ptr`.
100 *
101 * Returns NULL if `ch` is not found.
102 * Panics if `ptr` is NULL (undefined behaviour).
103 */
memchr(const void * ptr,int ch,size_t count)104 void *memchr(const void *ptr, int ch, size_t count)
105 {
106 size_t i;
107 const unsigned char *p = (const unsigned char *)ptr;
108
109 CHECK(ptr != NULL);
110
111 /* Iterate over at most `strsz` characters of `str`. */
112 for (i = 0; i < count; ++i) {
113 if (p[i] == (unsigned char)ch) {
114 return (void *)(&p[i]);
115 }
116 }
117
118 return NULL;
119 }
120
121 /**
122 * Returns the length of the null-terminated byte string `str`, examining at
123 * most `strsz` bytes.
124 *
125 * If `str` is a NULL pointer, it returns zero.
126 * If a NULL character is not found, it returns `strsz`.
127 */
strnlen_s(const char * str,size_t strsz)128 size_t strnlen_s(const char *str, size_t strsz)
129 {
130 if (str == NULL) {
131 return 0;
132 }
133
134 for (size_t i = 0; i < strsz; ++i) {
135 if (str[i] == '\0') {
136 return i;
137 }
138 }
139
140 /* NULL character not found. */
141 return strsz;
142 }
143