blob: 7a2db2dc908b7dbf01e173beae1ff39f8862cf68 [file] [log] [blame]
Andrew Scull2b5fbad2019-04-05 13:55:56 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
Andrew Scull2b5fbad2019-04-05 13:55:56 +01007 */
8
9#include "hf/std.h"
10
David Brazdil74e9c3b2019-08-28 11:09:08 +010011#include "hf/check.h"
Andrew Scull2b5fbad2019-04-05 13:55:56 +010012
13/* Declare unsafe functions locally so they are not available globally. */
14void *memset(void *s, int c, size_t n);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010015void *memcpy(void *dst, const void *src, size_t n);
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010016void *memmove(void *dst, const void *src, size_t n);
Andrew Scull2b5fbad2019-04-05 13:55:56 +010017
David Brazdil2246abe2019-08-23 12:21:06 +010018/*
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
Andrew Scull2b5fbad2019-04-05 13:55:56 +010036void memset_s(void *dest, rsize_t destsz, int ch, rsize_t count)
37{
David Brazdil2246abe2019-08-23 12:21:06 +010038 CHECK_OR_FILL(dest != NULL, dest, destsz, ch);
Andrew Scull2b5fbad2019-04-05 13:55:56 +010039
David Brazdil2246abe2019-08-23 12:21:06 +010040 /* Check count <= destsz <= RSIZE_MAX. */
41 CHECK_OR_FILL(destsz <= RSIZE_MAX, dest, destsz, ch);
42 CHECK_OR_FILL(count <= destsz, dest, destsz, ch);
Andrew Scull2b5fbad2019-04-05 13:55:56 +010043
Andrew Walbrane52006c2019-10-22 18:01:28 +010044 /*
45 * Clang analyzer doesn't like us calling unsafe memory functions, so
46 * make it ignore this call.
47 */
48#ifndef __clang_analyzer__
Andrew Scull2b5fbad2019-04-05 13:55:56 +010049 memset(dest, ch, count);
Andrew Walbrane52006c2019-10-22 18:01:28 +010050#endif
Andrew Scull2b5fbad2019-04-05 13:55:56 +010051}
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010052
53void memcpy_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
54{
55 uintptr_t d = (uintptr_t)dest;
56 uintptr_t s = (uintptr_t)src;
57
David Brazdil2246abe2019-08-23 12:21:06 +010058 CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
59 CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010060
David Brazdil2246abe2019-08-23 12:21:06 +010061 /* Check count <= destsz <= RSIZE_MAX. */
62 CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
63 CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010064
David Brazdil2246abe2019-08-23 12:21:06 +010065 /*
66 * Buffer overlap test.
67 * case a) `d < s` implies `s >= d+count`
68 * case b) `d > s` implies `d >= s+count`
69 */
70 CHECK_OR_ZERO_FILL(d != s, dest, destsz);
71 CHECK_OR_ZERO_FILL(d < s || d >= (s + count), dest, destsz);
72 CHECK_OR_ZERO_FILL(d > s || s >= (d + count), dest, destsz);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010073
Andrew Walbrane52006c2019-10-22 18:01:28 +010074#ifndef __clang_analyzer__
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010075 memcpy(dest, src, count);
Andrew Walbrane52006c2019-10-22 18:01:28 +010076#endif
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010077}
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010078
79void memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t count)
80{
David Brazdil2246abe2019-08-23 12:21:06 +010081 CHECK_OR_ZERO_FILL(dest != NULL, dest, destsz);
82 CHECK_OR_ZERO_FILL(src != NULL, dest, destsz);
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010083
David Brazdil2246abe2019-08-23 12:21:06 +010084 /* Check count <= destsz <= RSIZE_MAX. */
85 CHECK_OR_ZERO_FILL(destsz <= RSIZE_MAX, dest, destsz);
86 CHECK_OR_ZERO_FILL(count <= destsz, dest, destsz);
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010087
Andrew Walbrane52006c2019-10-22 18:01:28 +010088#ifndef __clang_analyzer__
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010089 memmove(dest, src, count);
Andrew Walbrane52006c2019-10-22 18:01:28 +010090#endif
Andrew Scull8fbd7ee2019-04-05 14:36:34 +010091}
Andrew Scull55baca62019-04-05 14:56:20 +010092
David Brazdil2246abe2019-08-23 12:21:06 +010093/**
David Brazdil74e9c3b2019-08-28 11:09:08 +010094 * Finds the first occurrence of character `ch` in the first `count` bytes of
95 * memory pointed to by `ptr`.
96 *
97 * Returns NULL if `ch` is not found.
98 * Panics if `ptr` is NULL (undefined behaviour).
99 */
100void *memchr(const void *ptr, int ch, size_t count)
101{
102 size_t i;
103 const unsigned char *p = (const unsigned char *)ptr;
104
105 CHECK(ptr != NULL);
106
107 /* Iterate over at most `strsz` characters of `str`. */
108 for (i = 0; i < count; ++i) {
109 if (p[i] == (unsigned char)ch) {
110 return (void *)(&p[i]);
111 }
112 }
113
114 return NULL;
115}
116
117/**
David Brazdil2246abe2019-08-23 12:21:06 +0100118 * Returns the length of the null-terminated byte string `str`, examining at
119 * most `strsz` bytes.
120 *
121 * If `str` is a NULL pointer, it returns zero.
122 * If a NULL character is not found, it returns `strsz`.
123 */
Andrew Scull55baca62019-04-05 14:56:20 +0100124size_t strnlen_s(const char *str, size_t strsz)
125{
Andrew Scull55baca62019-04-05 14:56:20 +0100126 if (str == NULL) {
127 return 0;
128 }
129
David Brazdil2246abe2019-08-23 12:21:06 +0100130 for (size_t i = 0; i < strsz; ++i) {
131 if (str[i] == '\0') {
132 return i;
133 }
Andrew Scull55baca62019-04-05 14:56:20 +0100134 }
135
David Brazdil2246abe2019-08-23 12:21:06 +0100136 /* NULL character not found. */
137 return strsz;
Andrew Scull55baca62019-04-05 14:56:20 +0100138}