From b0b92aeb3828219075fce23543fb39fee8608e99 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Tue, 25 Apr 2017 01:27:41 +0100 Subject: [PATCH] Poison non-POD memset & non-trivially-copyable memcpy/memmove MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch catches invalid initialization of non-POD types with memset, at compile time. This is what I used to catch the problems fixed by the previous patches in the series: $ make -k 2>&1 | grep "deleted function" src/gdb/breakpoint.c:951:53: error: use of deleted function ‘void* memset(T*, int, size_t) [with T = bp_location; = void; size_t = long unsigned int]’ src/gdb/breakpoint.c:7325:32: error: use of deleted function ‘void* memset(T*, int, size_t) [with T = bp_location; = void; size_t = long unsigned int]’ src/gdb/btrace.c:1153:42: error: use of deleted function ‘void* memset(T*, int, size_t) [with T = btrace_insn; = void; size_t = long unsigned int]’ ... gdb/ChangeLog: 2017-04-25 Pedro Alves * common/common-defs.h: Include "common/poison.h". * common/function-view.h: (Not, Or, Requires): Move to traits.h and adjust. * common/poison.h: New file. * common/traits.h: Include . (Not, Or, Requires): New, moved from common/function-view.h. --- gdb/ChangeLog | 9 +++++ gdb/common/common-defs.h | 1 + gdb/common/function-view.h | 40 ++---------------- gdb/common/poison.h | 83 ++++++++++++++++++++++++++++++++++++++ gdb/common/traits.h | 30 +++++++++++++- 5 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 gdb/common/poison.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index dc321a25f48..26e6370a375 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2017-04-25 Pedro Alves + + * common/common-defs.h: Include "common/poison.h". + * common/function-view.h: (Not, Or, Requires): Move to traits.h + and adjust. + * common/poison.h: New file. + * common/traits.h: Include . + (Not, Or, Requires): New, moved from common/function-view.h. + 2017-04-25 Pedro Alves * breakpoint.h (struct breakpoint): In-class initialize all diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h index af371114dce..439bce622f6 100644 --- a/gdb/common/common-defs.h +++ b/gdb/common/common-defs.h @@ -82,6 +82,7 @@ #include "common-debug.h" #include "cleanups.h" #include "common-exceptions.h" +#include "common/poison.h" #define EXTERN_C extern "C" #define EXTERN_C_PUSH extern "C" { diff --git a/gdb/common/function-view.h b/gdb/common/function-view.h index 66a691b5b39..de9a6345963 100644 --- a/gdb/common/function-view.h +++ b/gdb/common/function-view.h @@ -153,34 +153,6 @@ namespace gdb { -namespace traits { - /* A few trait helpers. */ - template - struct Not : public std::integral_constant - {}; - - template - struct Or; - - template<> - struct Or<> : public std::false_type - {}; - - template - struct Or : public B1 - {}; - - template - struct Or - : public std::conditional::type - {}; - - template - struct Or - : public std::conditional>::type - {}; -} /* namespace traits */ - namespace fv_detail { /* Bits shared by all function_view instantiations that do not depend on the template parameters. */ @@ -209,9 +181,9 @@ class function_view { template using CompatibleReturnType - = traits::Or, - std::is_same, - std::is_convertible>; + = Or, + std::is_same, + std::is_convertible>; /* True if Func can be called with Args, and either the result is Res, convertible to Res or Res is void. */ @@ -227,10 +199,6 @@ class function_view : std::is_same::type> {}; - /* Helper to make SFINAE logic easier to read. */ - template - using Requires = typename std::enable_if::type; - public: /* NULL by default. */ @@ -248,7 +216,7 @@ class function_view compatible. */ template >>, + typename = Requires>>, typename = Requires>> function_view (Callable &&callable) noexcept { diff --git a/gdb/common/poison.h b/gdb/common/poison.h new file mode 100644 index 00000000000..a875568d97c --- /dev/null +++ b/gdb/common/poison.h @@ -0,0 +1,83 @@ +/* Poison symbols at compile time. + + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef COMMON_POISON_H +#define COMMON_POISON_H + +#include "traits.h" + +/* Poison memset of non-POD types. The idea is catching invalid + initialization of non-POD structs that is easy to be introduced as + side effect of refactoring. For example, say this: + + struct S { VEC(foo_s) *m_data; }; + +is converted to this at some point: + + struct S { + S() { m_data.reserve (10); } + std::vector m_data; + }; + +and old code was initializing S objects like this: + + struct S s; + memset (&s, 0, sizeof (S)); // whoops, now wipes vector. + +Declaring memset as deleted for non-POD types makes the memset above +be a compile-time error. */ + +/* Helper for SFINAE. True if "T *" is memsettable. I.e., if T is + either void, or POD. */ +template +struct IsMemsettable + : gdb::Or, + std::is_pod> +{}; + +template >>> +void *memset (T *s, int c, size_t n) = delete; + +/* Similarly, poison memcpy and memmove of non trivially-copyable + types, which is undefined. */ + +/* True if "T *" is relocatable. I.e., copyable with memcpy/memmove. + I.e., T is either trivially copyable, or void. */ +template +struct IsRelocatable + : gdb::Or, + std::is_trivially_copyable> +{}; + +/* True if both source and destination are relocatable. */ + +template +using BothAreRelocatable + = gdb::And, IsRelocatable>; + +template >>> +void *memcpy (D *dest, const S *src, size_t n) = delete; + +template >>> +void *memmove (D *dest, const S *src, size_t n) = delete; + +#endif /* COMMON_POISON_H */ diff --git a/gdb/common/traits.h b/gdb/common/traits.h index c8f29cccbed..4f7161bdec4 100644 --- a/gdb/common/traits.h +++ b/gdb/common/traits.h @@ -32,7 +32,32 @@ template using void_t = typename make_void::type; /* A few trait helpers, mainly stolen from libstdc++. Uppercase - because "and" is a keyword. */ + because "and/or", etc. are reserved keywords. */ + +template +struct Not : public std::integral_constant +{}; + +template +struct Or; + +template<> +struct Or<> : public std::false_type +{}; + +template +struct Or : public B1 +{}; + +template +struct Or + : public std::conditional::type +{}; + +template +struct Or + : public std::conditional>::type +{}; template struct And; @@ -55,6 +80,9 @@ struct And : public std::conditional, B1>::type {}; +/* Concepts-light-like helper to make SFINAE logic easier to read. */ +template +using Requires = typename std::enable_if::type; } #endif /* COMMON_TRAITS_H */ -- 2.30.2