Poison non-POD memset & non-trivially-copyable memcpy/memmove
authorPedro Alves <palves@redhat.com>
Tue, 25 Apr 2017 00:27:41 +0000 (01:27 +0100)
committerPedro Alves <palves@redhat.com>
Tue, 25 Apr 2017 00:46:19 +0000 (01:46 +0100)
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; <template-parameter-1-2> = 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; <template-parameter-1-2> = 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; <template-parameter-1-2> = void; size_t = long unsigned int]’
...

gdb/ChangeLog:
2017-04-25  Pedro Alves  <palves@redhat.com>

* 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 <type_traits>.
(Not, Or, Requires): New, moved from common/function-view.h.

gdb/ChangeLog
gdb/common/common-defs.h
gdb/common/function-view.h
gdb/common/poison.h [new file with mode: 0644]
gdb/common/traits.h

index dc321a25f48d732a1a9e60978e0906b9071f0642..26e6370a375ef71b750f67a626cbebafdc7bb0c2 100644 (file)
@@ -1,3 +1,12 @@
+2017-04-25  Pedro Alves  <palves@redhat.com>
+
+       * 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 <type_traits>.
+       (Not, Or, Requires): New, moved from common/function-view.h.
+
 2017-04-25  Pedro Alves  <palves@redhat.com>
 
        * breakpoint.h (struct breakpoint): In-class initialize all
index af371114dce834ae0eb5b04dd7dc91a08dfc3979..439bce622f6b7bfbc9e3b95c5f275fa9290f44dd 100644 (file)
@@ -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" {
index 66a691b5b39f0573a3e3db792b116cc96dd103ba..de9a6345963ad93e8a712b431e670af95c99ec25 100644 (file)
 
 namespace gdb {
 
-namespace traits {
-  /* A few trait helpers.  */
-  template<typename Predicate>
-  struct Not : public std::integral_constant<bool, !Predicate::value>
-  {};
-
-  template<typename...>
-  struct Or;
-
-  template<>
-  struct Or<> : public std::false_type
-  {};
-
-  template<typename B1>
-  struct Or<B1> : public B1
-  {};
-
-  template<typename B1, typename B2>
-  struct Or<B1, B2>
-    : public std::conditional<B1::value, B1, B2>::type
-  {};
-
-  template<typename B1,typename B2,typename B3, typename... Bn>
-  struct Or<B1, B2, B3, Bn...>
-    : public std::conditional<B1::value, B1, Or<B2, B3, Bn...>>::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<Res (Args...)>
 {
   template<typename From, typename To>
   using CompatibleReturnType
-    = traits::Or<std::is_void<To>,
-                std::is_same<From, To>,
-                std::is_convertible<From, To>>;
+    = Or<std::is_void<To>,
+        std::is_same<From, To>,
+        std::is_convertible<From, To>>;
 
   /* 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<Res (Args...)>
     : std::is_same<function_view, typename std::decay<Callable>::type>
   {};
 
-  /* Helper to make SFINAE logic easier to read.  */
-  template<typename Condition>
-  using Requires = typename std::enable_if<Condition::value, void>::type;
-
  public:
 
   /* NULL by default.  */
@@ -248,7 +216,7 @@ class function_view<Res (Args...)>
      compatible.  */
   template
     <typename Callable,
-     typename = Requires<traits::Not<IsFunctionView<Callable>>>,
+     typename = Requires<Not<IsFunctionView<Callable>>>,
      typename = Requires<IsCompatibleCallable<Callable>>>
   function_view (Callable &&callable) noexcept
   {
diff --git a/gdb/common/poison.h b/gdb/common/poison.h
new file mode 100644 (file)
index 0000000..a875568
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.  */
+
+#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<foo> 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<typename T>
+struct IsMemsettable
+  : gdb::Or<std::is_void<T>,
+           std::is_pod<T>>
+{};
+
+template <typename T,
+         typename = gdb::Requires<gdb::Not<IsMemsettable<T>>>>
+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<typename T>
+struct IsRelocatable
+  : gdb::Or<std::is_void<T>,
+           std::is_trivially_copyable<T>>
+{};
+
+/* True if both source and destination are relocatable.  */
+
+template <typename D, typename S>
+using BothAreRelocatable
+  = gdb::And<IsRelocatable<D>, IsRelocatable<S>>;
+
+template <typename D, typename S,
+         typename = gdb::Requires<gdb::Not<BothAreRelocatable<D, S>>>>
+void *memcpy (D *dest, const S *src, size_t n) = delete;
+
+template <typename D, typename S,
+         typename = gdb::Requires<gdb::Not<BothAreRelocatable<D, S>>>>
+void *memmove (D *dest, const S *src, size_t n) = delete;
+
+#endif /* COMMON_POISON_H */
index c8f29cccbedbf5ad0b450f8766c8cbb72adc3632..4f7161bdec424abcb973c47cb47f3df5c182e484 100644 (file)
@@ -32,7 +32,32 @@ template<typename... Ts>
 using void_t = typename make_void<Ts...>::type;
 
 /* A few trait helpers, mainly stolen from libstdc++.  Uppercase
-   because "and" is a keyword.  */
+   because "and/or", etc. are reserved keywords.  */
+
+template<typename Predicate>
+struct Not : public std::integral_constant<bool, !Predicate::value>
+{};
+
+template<typename...>
+struct Or;
+
+template<>
+struct Or<> : public std::false_type
+{};
+
+template<typename B1>
+struct Or<B1> : public B1
+{};
+
+template<typename B1, typename B2>
+struct Or<B1, B2>
+  : public std::conditional<B1::value, B1, B2>::type
+{};
+
+template<typename B1,typename B2,typename B3, typename... Bn>
+struct Or<B1, B2, B3, Bn...>
+  : public std::conditional<B1::value, B1, Or<B2, B3, Bn...>>::type
+{};
 
 template<typename...>
 struct And;
@@ -55,6 +80,9 @@ struct And<B1, B2, B3, Bn...>
   : public std::conditional<B1::value, And<B2, B3, Bn...>, B1>::type
 {};
 
+/* Concepts-light-like helper to make SFINAE logic easier to read.  */
+template<typename Condition>
+using Requires = typename std::enable_if<Condition::value, void>::type;
 }
 
 #endif /* COMMON_TRAITS_H */