mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
Poison non-POD memset & non-trivially-copyable memcpy/memmove
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.
This commit is contained in:
parent
16c4d54a71
commit
b0b92aeb38
@ -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
|
||||
|
@ -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" {
|
||||
|
@ -153,34 +153,6 @@
|
||||
|
||||
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
|
||||
{
|
||||
|
83
gdb/common/poison.h
Normal file
83
gdb/common/poison.h
Normal file
@ -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 */
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user