From c73fd34d976da6886c812945e698e51497b92d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 16 Dec 2015 00:50:36 +0100 Subject: [PATCH] Implement a memcpy_s() equivalent. --- configure.ac | 2 +- include/cryb/Makefile.am | 1 + include/cryb/memcpy_s.h | 40 ++++++ lib/core/Makefile.am | 1 + lib/core/cryb_memcpy_s.c | 77 ++++++++++ t/.gitignore | 1 + t/Makefile.am | 5 +- t/t_memcpy_s.c | 304 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 include/cryb/memcpy_s.h create mode 100644 lib/core/cryb_memcpy_s.c create mode 100644 t/t_memcpy_s.c diff --git a/configure.ac b/configure.ac index 39e5174..45022c9 100644 --- a/configure.ac +++ b/configure.ac @@ -118,7 +118,7 @@ AC_CHECK_FUNCS([setrlimit]) # C11 features # XXX our version has an incorrect prototype due to the lack of a test # for the existence of rsize_t and RSIZE_MAX. -AC_CHECK_FUNCS([memset_s]) +AC_CHECK_FUNCS([memcpy_s memset_s]) ############################################################################ # diff --git a/include/cryb/Makefile.am b/include/cryb/Makefile.am index 40abdb4..4926d27 100644 --- a/include/cryb/Makefile.am +++ b/include/cryb/Makefile.am @@ -22,6 +22,7 @@ cryb_HEADERS += \ ctype.h \ defs.h \ endian.h \ + memcpy_s.h \ memset_s.h \ strchrnul.h \ string.h \ diff --git a/include/cryb/memcpy_s.h b/include/cryb/memcpy_s.h new file mode 100644 index 0000000..6fa6a34 --- /dev/null +++ b/include/cryb/memcpy_s.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2015 The University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CRYB_MEMCPY_S_H_INCLUDED +#define CRYB_MEMCPY_S_H_INCLUDED + +int cryb_memcpy_s(void *, size_t, const void *, size_t); + +#if !HAVE_MEMCPY_S +#undef memcpy_s +#define memcpy_s(arg, ...) cryb_memcpy_s(arg, __VA_ARGS__) +#endif + +#endif diff --git a/lib/core/Makefile.am b/lib/core/Makefile.am index 95ddb28..34dfcec 100644 --- a/lib/core/Makefile.am +++ b/lib/core/Makefile.am @@ -4,6 +4,7 @@ lib_LTLIBRARIES = libcryb-core.la libcryb_core_la_SOURCES = \ cryb_assert.c \ + cryb_memcpy_s.c \ cryb_memset_s.c \ cryb_strchrnul.c \ cryb_string.c \ diff --git a/lib/core/cryb_memcpy_s.c b/lib/core/cryb_memcpy_s.c new file mode 100644 index 0000000..3ecc7b6 --- /dev/null +++ b/lib/core/cryb_memcpy_s.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2015 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include + +/* + * Like memcpy(), but checks for overflow and overwrites the destination + * range with zeroes on error. + */ +int +cryb_memcpy_s(void *d, size_t dsz, const void *s, size_t n) +{ + unsigned int i; + + /* unrecoverable errors */ + if (d == NULL) + return (EINVAL); +CRYB_DISABLE_COVERAGE + if (dsz > SIZE_MAX) + return (ERANGE); +CRYB_RESTORE_COVERAGE + /* recoverable errors */ + if (s == NULL || n > dsz || +CRYB_DISABLE_COVERAGE + n > SIZE_MAX || +CRYB_RESTORE_COVERAGE + (s >= d && s < d + dsz) || (d >= s && d < s + n)) { + memset_s(d, dsz, 0, dsz); + if (n > dsz) + return (EOVERFLOW); +CRYB_DISABLE_COVERAGE + if (n > SIZE_MAX) + return (ERANGE); +CRYB_RESTORE_COVERAGE + return (EINVAL); + } + for (i = 0; i < dsz && i < n; ++i) + ((volatile unsigned char *)d)[i] = ((unsigned char *)s)[i]; + return (0); +} diff --git a/t/.gitignore b/t/.gitignore index 9bd7ad9..775a177 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -29,6 +29,7 @@ /t_md4_openssl /t_md5 /t_md5_openssl +/t_memcpy_s /t_memset_s /t_mpi /t_mpi_addsub diff --git a/t/Makefile.am b/t/Makefile.am index 033c672..67c084b 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -94,9 +94,12 @@ TESTS += t_core t_core_LDADD = $(libt) $(libcore) TESTS += t_assert t_assert_LDADD = $(libt) $(libcore) -TESTS += t_ctype t_endian t_memset_s t_strchrnul t_strlcat t_strlcmp t_strlcpy +TESTS += t_ctype t_endian t_memcpy_s t_memset_s t_strchrnul t_strlcat t_strlcmp t_strlcpy +TESTS += t_string t_wstring +EXTRA_DIST += t__string.c t_ctype_LDADD = $(libt) $(libcore) t_endian_LDADD = $(libt) $(libcore) +t_memcpy_s_LDADD = $(libt) $(libcore) t_memset_s_LDADD = $(libt) $(libcore) t_strchrnul_LDADD = $(libt) $(libcore) t_strlcat_LDADD = $(libt) $(libcore) diff --git a/t/t_memcpy_s.c b/t/t_memcpy_s.c new file mode 100644 index 0000000..057d2ff --- /dev/null +++ b/t/t_memcpy_s.c @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 2015 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include +#include +#include + +#undef HAVE_MEMCPY_S +#include +#include +#include + +#define T_BUF_LEN 40 + +struct t_plain_case { + const char *desc; + const char in[T_BUF_LEN]; + size_t dsz; + const char s[T_BUF_LEN]; + size_t len; + const char out[T_BUF_LEN]; + size_t outlen; + int ret; +}; + +/*************************************************************************** + * Plain test cases + */ +static struct t_plain_case t_plain_cases[] = { + { + .desc = "zero", + .in = "squeamish ossifrage", + .dsz = sizeof "squeamish ossifrage" - 1, + .s = "", + .len = 0, + .out = "squeamish ossifrage", + .ret = 0, + }, + { + .desc = "short", + .in = "squeamish ossifrage", + .dsz = sizeof "squeamish ossifrage" - 1, + .s = "the quick", + .len = sizeof "the quick" - 1, + .out = "the quick ossifrage", + .ret = 0, + }, + { + .desc = "exact", + .in = "squeamish ossifrage", + .dsz = sizeof "squeamish ossifrage" - 1, + .s = "the quick brown fox", + .len = sizeof "the quick brown fox" - 1, + .out = "the quick brown fox", + .ret = 0, + }, + { + .desc = "long", + .in = "squeamish ossifrage", + .dsz = sizeof "squeamish ossifrage" - 1, + .s = "the quick brown fox jumps", + .len = sizeof "the quick brown fox jumps" + 1, + .out = "", + .ret = EOVERFLOW, + }, +}; + +static int +t_memcpy_s(char **desc CRYB_UNUSED, void *arg) +{ + struct t_plain_case *t = arg; + char buf[T_BUF_LEN]; + int ret; + + memset(buf, 0, sizeof buf); + strlcpy(buf, t->in, T_BUF_LEN); + ret = memcpy_s(buf, t->dsz, t->s, t->len); + return (t_compare_i(t->ret, ret) & + t_compare_mem(t->out, buf, T_BUF_LEN)); +} + +/*************************************************************************** + * Overlapping test cases + */ + +struct t_overlap_case { + const char *desc; + const char *in; + size_t s_off, s_len, d_off, d_len; + const char out[T_BUF_LEN]; + int ret; +}; + +static struct t_overlap_case t_overlap_cases[] = { + { + /* [<<<<<<<<<< >>>>>>>>>>] */ + .desc = "left, disjoint", + .in = "the magic words are squeamish ossifrage", + .s_off = 0, + .s_len = 15, + .d_off = 16, + .d_len = 23, + .out = "the magic words the magic wordsssifrage", + .ret = 0, + }, + { + /* [<<<<<<<<<<>>>>>>>>>>] */ + .desc = "left, adjoining", + .in = "the magic words are squeamish ossifrage", + .s_off = 0, + .s_len = 16, + .d_off = 16, + .d_len = 23, + .out = "the magic words the magic words sifrage", + .ret = 0, + }, + { + /* [>>>>>>>>>> <<<<<<<<<<] */ + .desc = "right, disjoint", + .in = "the magic words are squeamish ossifrage", + .s_off = 20, + .s_len = 19, + .d_off = 0, + .d_len = 19, + .out = "squeamish ossifrage squeamish ossifrage", + .ret = 0, + }, + { + /* [>>>>>>>>>><<<<<<<<<<] */ + .desc = "right, adjoining", + .in = "the magic words are squeamish ossifrage", + .s_off = 20, + .s_len = 19, + .d_off = 0, + .d_len = 19, + .out = "squeamish ossifrage squeamish ossifrage", + .ret = 0, + }, + { + /* [<<<<<<<>>>>>>>] */ + .desc = "left, overlapping", + .in = "the magic words are squeamish ossifrage", + .s_off = 4, + .s_len = 15, + .d_off = 16, + .d_len = 23, + .out = "the magic words ", + .ret = EINVAL, + }, + { + /* [>>>>>>>>XXXX<<<<<<<<] */ + .desc = "right, overlapping", + .in = "the magic words are squeamish ossifrage", + .s_off = 16, + .s_len = 23, + .d_off = 0, + .d_len = 29, + .out = "\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0" + " ossifrage", + .ret = EINVAL, + }, + { + /* [>>>>>>>>XXXX>>>>>>>>] */ + .desc = "inside", + .in = "the magic words are squeamish ossifrage", + .s_off = 10, + .s_len = 19, + .d_off = 0, + .d_len = 39, + .out = "", + .ret = EINVAL, + }, + { + /* [<<<<<<< sz before overlap is noticed */ + .desc = "outside", + .in = "the magic words are squeamish ossifrage", + .s_off = 0, + .s_len = 39, + .d_off = 10, + .d_len = 19, + .out = "the magic " + "\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0" + " ossifrage", + .ret = EOVERFLOW, + }, + { + /* [XXXXXXXXXXXXXXXXXXXX] */ + .desc = "full overlap", + .in = "the magic words are squeamish ossifrage", + .s_off = 10, + .s_len = 19, + .d_off = 10, + .d_len = 19, + .out = "the magic " + "\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0" + " ossifrage", + .ret = EINVAL, + }, +}; + +static int +t_memcpy_s_overlap(char **desc CRYB_UNUSED, void *arg) +{ + struct t_overlap_case *t = arg; + char buf[T_BUF_LEN]; + int ret; + + strlcpy(buf, t->in, sizeof buf); + ret = memcpy_s(buf + t->d_off, t->d_len, buf + t->s_off, t->s_len); + return (t_compare_i(t->ret, ret) & + t_compare_mem(t->out, buf, T_BUF_LEN)); +} + +/*************************************************************************** + * NULL cases + */ +static int +t_memcpy_s_null_d(char **desc CRYB_UNUSED, void *arg) +{ + char buf[T_BUF_LEN]; + int ret; + + (void)arg; + memset(buf, 'x', sizeof buf); + ret = memcpy_s(NULL, sizeof buf, buf, sizeof buf); + return (t_compare_i(EINVAL, ret)); +} + +static int +t_memcpy_s_null_s(char **desc CRYB_UNUSED, void *arg) +{ + char buf[T_BUF_LEN]; + int ret; + + (void)arg; + memset(buf, 0, sizeof buf); + ret = memcpy_s(buf, sizeof buf, NULL, sizeof buf); + return (t_compare_i(EINVAL, ret) & + t_compare_mem(buf, t_zero, sizeof buf)); +} + + +/*************************************************************************** + * Boilerplate + */ + +int +t_prepare(int argc, char *argv[]) +{ + int i, n; + + (void)argc; + (void)argv; + t_add_test(t_memcpy_s_null_s, NULL, "null source"); + t_add_test(t_memcpy_s_null_d, NULL, "null destination"); + n = sizeof t_plain_cases / sizeof t_plain_cases[0]; + for (i = 0; i < n; ++i) + t_add_test(t_memcpy_s, &t_plain_cases[i], + t_plain_cases[i].desc); + n = sizeof t_overlap_cases / sizeof t_overlap_cases[0]; + for (i = 0; i < n; ++i) + t_add_test(t_memcpy_s_overlap, &t_overlap_cases[i], + t_overlap_cases[i].desc); + return (0); +} + +void +t_cleanup(void) +{ +}