/*
* Copyright 2014-2016 by Open Source Security, Inc., Brad Spengler <spender@grsecurity.net>
* and PaX Team <pageexec@freemail.hu>
* Licensed under the GPL v2
*
* Note: the choice of the license means that the compilation process is
* NOT 'eligible' as defined by gcc's library exception to the GPL v3,
* but for the kernel it doesn't matter since it doesn't link against
* any of the gcc libraries
*
* Usage:
* $ # for 4.5/4.6/C based 4.7
* $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
* $ # for C++ based 4.7/4.8+
* $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
* $ gcc -fplugin=./randomize_layout_plugin.so test.c -O2
*/
#include "gcc-common.h"
#include "randomize_layout_seed.h"
#if BUILDING_GCC_MAJOR < 4 || (BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR < 7)
#error "The RANDSTRUCT plugin requires GCC 4.7 or newer."
#endif
#define ORIG_TYPE_NAME(node) \
(TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS__)
#define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__)
__visible int plugin_is_GPL_compatible;
static int performance_mode;
static struct plugin_info randomize_layout_plugin_info = {
.version = "201402201816vanilla",
.help = "disable\t\t\tdo not activate plugin\n"
"performance-mode\tenable cacheline-aware layout randomization\n"
};
struct whitelist_entry {
const char *pathname;
const char *lhs;
const char *rhs;
};
static const struct whitelist_entry whitelist[] = {
/* NIU overloads mapping with page struct */
{ "drivers/net/ethernet/sun/niu.c", "page", "address_space" },
/* unix_skb_parms via UNIXCB() buffer */
{ "net/unix/af_unix.c", "unix_skb_parms", "char" },
/* big_key payload.data struct splashing */
{ "security/keys/big_key.c", "path", "void *" },
/* walk struct security_hook_heads as an array of struct hlist_head */
{ "security/security.c", "hlist_head", "security_hook_heads" },
{ }
};
/* from old Linux dcache.h */
static inline unsigned long
partial_name_hash(unsigned long c, unsigned long prevhash)
{
return (prevhash + (c << 4) + (c >> 4)) * 11;
}
static inline unsigned int
name_hash(const unsigned char *name)
{
unsigned long hash = 0;
unsigned int len = strlen((const char *)name);
while (len--)
hash = partial_name_hash(*name++, hash);
return (unsigned int)hash;
}
static tree handle_randomize_layout_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
{
tree type;
*no_add_attrs = true;
if (TREE_CODE(*node) == FUNCTION_DECL) {
error("%qE attribute does not apply to functions (%qF)", name, *node);
return NULL_TREE;
}
if (TREE_CODE(*node) == PARM_DECL) {
error("%qE attribute does not apply to function parameters (%qD)", name, *node);
return NULL_TREE;
}
if (TREE_CODE(*node) == VAR_DECL) {
error("%qE attribute does not apply to variables (%qD)", name, *node);
return NULL_TREE;
}
if (TYPE_P(*node)) {
type = *node;
} else {
gcc_assert(TREE_CODE(*node) == TYPE_DECL);
type = TREE_TYPE(*node);
}
if (TREE_CODE(type) != RECORD_TYPE) {
error("%qE attribute used on %qT applies to struct types only", name, type);
return NULL_TREE;
}
if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(type))) {
error("%qE attribute is already applied to the type %qT", name, type);
return NULL_TREE;
}
*no_add_attrs = false;
return NULL_TREE;
}
/* set on complete types that we don't need to inspect further at all */
static tree handle_randomize_considered_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
{
*no_add_attrs = false;
return NULL_TREE;
}
/*
* set on types that we've performed a shuffle on, to prevent re-shuffling
* this does not preclude us from inspecting its fields for potential shuffles
*/
static tree handle_randomize_performed_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
{
*no_add_attrs = false;
return NULL_TREE;
}
/*
* 64bit variant of Bob Jenkins' public domain PRNG
* 256 bits of internal state
*/
typedef unsigned long long u64;
typedef struct ranctx { u64 a; u64 b; u64 c; u64 d; } ranctx;
#define rot(x,k) (((x)<<(k))|((x)>>(64-(k))))
static u64 ranval(ranctx *x) {
u64 e = x->a - rot(x->