2017-05-06 09:37:45 +03:00
/*
* 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 \t do not activate plugin \n "
" performance-mode \t enable cacheline-aware layout randomization \n "
} ;
struct whitelist_entry {
const char * pathname ;
const char * lhs ;
const char * rhs ;
} ;
static const struct whitelist_entry whitelist [ ] = {
2017-05-26 07:44:38 +03:00
/* NIU overloads mapping with page struct */
{ " drivers/net/ethernet/sun/niu.c " , " page " , " address_space " } ,
2017-04-05 07:35:22 +03:00
/* unix_skb_parms via UNIXCB() buffer */
{ " net/unix/af_unix.c " , " unix_skb_parms " , " char " } ,
2017-05-26 07:40:22 +03:00
/* big_key payload.data struct splashing */
{ " security/keys/big_key.c " , " path " , " void * " } ,
2018-03-29 04:28:23 +03:00
/* walk struct security_hook_heads as an array of struct hlist_head */
{ " security/security.c " , " hlist_head " , " security_hook_heads " } ,
2017-05-06 09:37:45 +03:00
{ }
} ;
/* 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 ;
}
/*
* 64 bit 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 - > b , 7 ) ;
x - > a = x - > b ^ rot ( x - > c , 13 ) ;
x - > b = x - > c + rot ( x - > d , 37 ) ;
x - > c = x - > d + e ;
x - > d = e + x - > a ;
return x - > d ;
}
static void raninit ( ranctx * x , u64 * seed ) {
int i ;
x - > a = seed [ 0 ] ;
x - > b = seed [ 1 ] ;
x - > c = seed [ 2 ] ;
x - > d = seed [ 3 ] ;
for ( i = 0 ; i < 30 ; + + i )
( void ) ranval ( x ) ;
}
static u64 shuffle_seed [ 4 ] ;
struct partition_group {
tree tree_start ;
unsigned long start ;
unsigned long length ;
} ;
static void partition_struct ( tree * fields , unsigned long length , struct partition_group * size_groups , unsigned long * num_groups )
{
unsigned long i ;
unsigned long accum_size = 0 ;
unsigned long accum_length = 0 ;
unsigned long group_idx = 0 ;
gcc_assert ( length < INT_MAX ) ;
memset ( size_groups , 0 , sizeof ( struct partition_group ) * length ) ;
for ( i = 0 ; i < length ; i + + ) {
if ( size_groups [ group_idx ] . tree_start = = NULL_TREE ) {
size_groups [ group_idx ] . tree_start = fields [ i ] ;
size_groups [ group_idx ] . start = i ;
accum_length = 0 ;
accum_size = 0 ;
}
accum_size + = ( unsigned long ) int_size_in_bytes ( TREE_TYPE ( fields [ i ] ) ) ;
accum_length + + ;
if ( accum_size > = 64 ) {
size_groups [ group_idx ] . length = accum_length ;
accum_length = 0 ;
group_idx + + ;
}
}
if ( size_groups [ group_idx ] . tree_start ! = NULL_TREE & &
! size_groups [ group_idx ] . length ) {
size_groups [ group_idx ] . length = accum_length ;
group_idx + + ;
}
* num_groups = group_idx ;
}
static void performance_shuffle ( tree * newtree , unsigned long length , ranctx * prng_state )
{
unsigned long i , x ;
struct partition_group size_group [ length ] ;
unsigned long num_groups = 0 ;
unsigned long randnum ;
partition_struct ( newtree , length , ( struct partition_group * ) & size_group , & num_groups ) ;
for ( i = num_groups - 1 ; i > 0 ; i - - ) {
struct partition_group tmp ;
randnum = ranval ( prng_state ) % ( i + 1 ) ;
tmp = size_group [ i ] ;
size_group [ i ] = size_group [ randnum ] ;
size_group [ randnum ] = tmp ;
}
for ( x = 0 ; x < num_groups ; x + + ) {
for ( i = size_group [ x ] . start + size_group [ x ] . length - 1 ; i > size_group [ x ] . start ; i - - ) {
tree tmp ;
if ( DECL_BIT_FIELD_TYPE ( newtree [ i ] ) )
continue ;
randnum = ranval ( prng_state ) % ( i + 1 ) ;
// we could handle this case differently if desired
if ( DECL_BIT_FIELD_TYPE ( newtree [ randnum ] ) )
continue ;
tmp = newtree [ i ] ;
newtree [ i ] = newtree [ randnum ] ;
newtree [ randnum ] = tmp ;
}
}
}
static void full_shuffle ( tree * newtree , unsigned long length , ranctx * prng_state )
{
unsigned long i , randnum ;
for ( i = length - 1 ; i > 0 ; i - - ) {
tree tmp ;
randnum = ranval ( prng_state ) % ( i + 1 ) ;
tmp = newtree [ i ] ;
newtree [ i ] = newtree [ randnum ] ;
newtree [ randnum ] = tmp ;
}
}
/* modern in-place Fisher-Yates shuffle */
static void shuffle ( const_tree type , tree * newtree , unsigned long length )
{
unsigned long i ;
u64 seed [ 4 ] ;
ranctx prng_state ;
const unsigned char * structname ;
if ( length = = 0 )
return ;
gcc_assert ( TREE_CODE ( type ) = = RECORD_TYPE ) ;
structname = ORIG_TYPE_NAME ( type ) ;
# ifdef __DEBUG_PLUGIN
fprintf ( stderr , " Shuffling struct %s %p \n " , ( const char * ) structname , type ) ;
# ifdef __DEBUG_VERBOSE
debug_tree ( ( tree ) type ) ;
# endif
# endif
for ( i = 0 ; i < 4 ; i + + ) {
seed [ i ] = shuffle_seed [ i ] ;
seed [ i ] ^ = name_hash ( structname ) ;
}
raninit ( & prng_state , ( u64 * ) & seed ) ;
if ( performance_mode )
performance_shuffle ( newtree , length , & prng_state ) ;
else
full_shuffle ( newtree , length , & prng_state ) ;
}
static bool is_flexible_array ( const_tree field )
{
const_tree fieldtype ;
const_tree typesize ;
const_tree elemtype ;
const_tree elemsize ;
fieldtype = TREE_TYPE ( field ) ;
typesize = TYPE_SIZE ( fieldtype ) ;
if ( TREE_CODE ( fieldtype ) ! = ARRAY_TYPE )
return false ;
elemtype = TREE_TYPE ( fieldtype ) ;
elemsize = TYPE_SIZE ( elemtype ) ;
/* size of type is represented in bits */
if ( typesize = = NULL_TREE & & TYPE_DOMAIN ( fieldtype ) ! = NULL_TREE & &
TYPE_MAX_VALUE ( TYPE_DOMAIN ( fieldtype ) ) = = NULL_TREE )
return true ;
if ( typesize ! = NULL_TREE & &
( TREE_CONSTANT ( typesize ) & & ( ! tree_to_uhwi ( typesize ) | |
tree_to_uhwi ( typesize ) = = tree_to_uhwi ( elemsize ) ) ) )
return true ;
return false ;
}
static int relayout_struct ( tree type )
{
unsigned long num_fields = ( unsigned long ) list_length ( TYPE_FIELDS ( type ) ) ;
unsigned long shuffle_length = num_fields ;
tree field ;
tree newtree [ num_fields ] ;
unsigned long i ;
tree list ;
tree variant ;
tree main_variant ;
expanded_location xloc ;
bool has_flexarray = false ;
if ( TYPE_FIELDS ( type ) = = NULL_TREE )
return 0 ;
if ( num_fields < 2 )
return 0 ;
gcc_assert ( TREE_CODE ( type ) = = RECORD_TYPE ) ;
gcc_assert ( num_fields < INT_MAX ) ;
if ( lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( type ) ) | |
lookup_attribute ( " no_randomize_layout " , TYPE_ATTRIBUTES ( TYPE_MAIN_VARIANT ( type ) ) ) )
return 0 ;
/* Workaround for 3rd-party VirtualBox source that we can't modify ourselves */
if ( ! strcmp ( ( const char * ) ORIG_TYPE_NAME ( type ) , " INTNETTRUNKFACTORY " ) | |
! strcmp ( ( const char * ) ORIG_TYPE_NAME ( type ) , " RAWPCIFACTORY " ) )
return 0 ;
/* throw out any structs in uapi */
xloc = expand_location ( DECL_SOURCE_LOCATION ( TYPE_FIELDS ( type ) ) ) ;
if ( strstr ( xloc . file , " /uapi/ " ) )
error ( G_ ( " attempted to randomize userland API struct %s " ) , ORIG_TYPE_NAME ( type ) ) ;
for ( field = TYPE_FIELDS ( type ) , i = 0 ; field ; field = TREE_CHAIN ( field ) , i + + ) {
gcc_assert ( TREE_CODE ( field ) = = FIELD_DECL ) ;
newtree [ i ] = field ;
}
/*
* enforce that we don ' t randomize the layout of the last
* element of a struct if it ' s a 0 or 1 - length array
* or a proper flexible array
*/
if ( is_flexible_array ( newtree [ num_fields - 1 ] ) ) {
has_flexarray = true ;
shuffle_length - - ;
}
shuffle ( type , ( tree * ) newtree , shuffle_length ) ;
/*
* set up a bogus anonymous struct field designed to error out on unnamed struct initializers
* as gcc provides no other way to detect such code
*/
list = make_node ( FIELD_DECL ) ;
TREE_CHAIN ( list ) = newtree [ 0 ] ;
TREE_TYPE ( list ) = void_type_node ;
DECL_SIZE ( list ) = bitsize_zero_node ;
DECL_NONADDRESSABLE_P ( list ) = 1 ;
DECL_FIELD_BIT_OFFSET ( list ) = bitsize_zero_node ;
DECL_SIZE_UNIT ( list ) = size_zero_node ;
DECL_FIELD_OFFSET ( list ) = size_zero_node ;
DECL_CONTEXT ( list ) = type ;
// to satisfy the constify plugin
TREE_READONLY ( list ) = 1 ;
for ( i = 0 ; i < num_fields - 1 ; i + + )
TREE_CHAIN ( newtree [ i ] ) = newtree [ i + 1 ] ;
TREE_CHAIN ( newtree [ num_fields - 1 ] ) = NULL_TREE ;
main_variant = TYPE_MAIN_VARIANT ( type ) ;
for ( variant = main_variant ; variant ; variant = TYPE_NEXT_VARIANT ( variant ) ) {
TYPE_FIELDS ( variant ) = list ;
TYPE_ATTRIBUTES ( variant ) = copy_list ( TYPE_ATTRIBUTES ( variant ) ) ;
TYPE_ATTRIBUTES ( variant ) = tree_cons ( get_identifier ( " randomize_performed " ) , NULL_TREE , TYPE_ATTRIBUTES ( variant ) ) ;
TYPE_ATTRIBUTES ( variant ) = tree_cons ( get_identifier ( " designated_init " ) , NULL_TREE , TYPE_ATTRIBUTES ( variant ) ) ;
if ( has_flexarray )
TYPE_ATTRIBUTES ( type ) = tree_cons ( get_identifier ( " has_flexarray " ) , NULL_TREE , TYPE_ATTRIBUTES ( type ) ) ;
}
/*
* force a re - layout of the main variant
* the TYPE_SIZE for all variants will be recomputed
* by finalize_type_size ( )
*/
TYPE_SIZE ( main_variant ) = NULL_TREE ;
layout_type ( main_variant ) ;
gcc_assert ( TYPE_SIZE ( main_variant ) ! = NULL_TREE ) ;
return 1 ;
}
/* from constify plugin */
static const_tree get_field_type ( const_tree field )
{
return strip_array_types ( TREE_TYPE ( field ) ) ;
}
/* from constify plugin */
static bool is_fptr ( const_tree fieldtype )
{
if ( TREE_CODE ( fieldtype ) ! = POINTER_TYPE )
return false ;
return TREE_CODE ( TREE_TYPE ( fieldtype ) ) = = FUNCTION_TYPE ;
}
/* derived from constify plugin */
static int is_pure_ops_struct ( const_tree node )
{
const_tree field ;
gcc_assert ( TREE_CODE ( node ) = = RECORD_TYPE | | TREE_CODE ( node ) = = UNION_TYPE ) ;
for ( field = TYPE_FIELDS ( node ) ; field ; field = TREE_CHAIN ( field ) ) {
const_tree fieldtype = get_field_type ( field ) ;
enum tree_code code = TREE_CODE ( fieldtype ) ;
if ( node = = fieldtype )
continue ;
2019-07-27 18:58:41 +03:00
if ( code = = RECORD_TYPE | | code = = UNION_TYPE ) {
if ( ! is_pure_ops_struct ( fieldtype ) )
return 0 ;
2017-05-06 09:37:45 +03:00
continue ;
2019-07-27 18:58:41 +03:00
}
2017-05-06 09:37:45 +03:00
2019-07-27 18:58:41 +03:00
if ( ! is_fptr ( fieldtype ) )
2017-05-06 09:37:45 +03:00
return 0 ;
}
return 1 ;
}
static void randomize_type ( tree type )
{
tree variant ;
gcc_assert ( TREE_CODE ( type ) = = RECORD_TYPE ) ;
if ( lookup_attribute ( " randomize_considered " , TYPE_ATTRIBUTES ( type ) ) )
return ;
if ( lookup_attribute ( " randomize_layout " , TYPE_ATTRIBUTES ( TYPE_MAIN_VARIANT ( type ) ) ) | | is_pure_ops_struct ( type ) )
relayout_struct ( type ) ;
for ( variant = TYPE_MAIN_VARIANT ( type ) ; variant ; variant = TYPE_NEXT_VARIANT ( variant ) ) {
TYPE_ATTRIBUTES ( type ) = copy_list ( TYPE_ATTRIBUTES ( type ) ) ;
TYPE_ATTRIBUTES ( type ) = tree_cons ( get_identifier ( " randomize_considered " ) , NULL_TREE , TYPE_ATTRIBUTES ( type ) ) ;
}
# ifdef __DEBUG_PLUGIN
fprintf ( stderr , " Marking randomize_considered on struct %s \n " , ORIG_TYPE_NAME ( type ) ) ;
# ifdef __DEBUG_VERBOSE
debug_tree ( type ) ;
# endif
# endif
}
static void update_decl_size ( tree decl )
{
tree lastval , lastidx , field , init , type , flexsize ;
unsigned HOST_WIDE_INT len ;
type = TREE_TYPE ( decl ) ;
if ( ! lookup_attribute ( " has_flexarray " , TYPE_ATTRIBUTES ( type ) ) )
return ;
init = DECL_INITIAL ( decl ) ;
if ( init = = NULL_TREE | | init = = error_mark_node )
return ;
if ( TREE_CODE ( init ) ! = CONSTRUCTOR )
return ;
len = CONSTRUCTOR_NELTS ( init ) ;
if ( ! len )
return ;
lastval = CONSTRUCTOR_ELT ( init , CONSTRUCTOR_NELTS ( init ) - 1 ) - > value ;
lastidx = CONSTRUCTOR_ELT ( init , CONSTRUCTOR_NELTS ( init ) - 1 ) - > index ;
for ( field = TYPE_FIELDS ( TREE_TYPE ( decl ) ) ; TREE_CHAIN ( field ) ; field = TREE_CHAIN ( field ) )
;
if ( lastidx ! = field )
return ;
if ( TREE_CODE ( lastval ) ! = STRING_CST ) {
error ( " Only string constants are supported as initializers "
" for randomized structures with flexible arrays " ) ;
return ;
}
flexsize = bitsize_int ( TREE_STRING_LENGTH ( lastval ) *
tree_to_uhwi ( TYPE_SIZE ( TREE_TYPE ( TREE_TYPE ( lastval ) ) ) ) ) ;
DECL_SIZE ( decl ) = size_binop ( PLUS_EXPR , TYPE_SIZE ( type ) , flexsize ) ;
return ;
}
static void randomize_layout_finish_decl ( void * event_data , void * data )
{
tree decl = ( tree ) event_data ;
tree type ;
if ( decl = = NULL_TREE | | decl = = error_mark_node )
return ;
type = TREE_TYPE ( decl ) ;
if ( TREE_CODE ( decl ) ! = VAR_DECL )
return ;
if ( TREE_CODE ( type ) ! = RECORD_TYPE & & TREE_CODE ( type ) ! = UNION_TYPE )
return ;
if ( ! lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( type ) ) )
return ;
DECL_SIZE ( decl ) = 0 ;
DECL_SIZE_UNIT ( decl ) = 0 ;
SET_DECL_ALIGN ( decl , 0 ) ;
SET_DECL_MODE ( decl , VOIDmode ) ;
SET_DECL_RTL ( decl , 0 ) ;
update_decl_size ( decl ) ;
layout_decl ( decl , 0 ) ;
}
static void finish_type ( void * event_data , void * data )
{
tree type = ( tree ) event_data ;
if ( type = = NULL_TREE | | type = = error_mark_node )
return ;
if ( TREE_CODE ( type ) ! = RECORD_TYPE )
return ;
if ( TYPE_FIELDS ( type ) = = NULL_TREE )
return ;
if ( lookup_attribute ( " randomize_considered " , TYPE_ATTRIBUTES ( type ) ) )
return ;
# ifdef __DEBUG_PLUGIN
fprintf ( stderr , " Calling randomize_type on %s \n " , ORIG_TYPE_NAME ( type ) ) ;
# endif
# ifdef __DEBUG_VERBOSE
debug_tree ( type ) ;
# endif
randomize_type ( type ) ;
return ;
}
2018-02-06 04:27:46 +03:00
static struct attribute_spec randomize_layout_attr = { } ;
static struct attribute_spec no_randomize_layout_attr = { } ;
static struct attribute_spec randomize_considered_attr = { } ;
static struct attribute_spec randomize_performed_attr = { } ;
2017-05-06 09:37:45 +03:00
2018-02-06 04:27:46 +03:00
static void register_attributes ( void * event_data , void * data )
{
randomize_layout_attr . name = " randomize_layout " ;
randomize_layout_attr . type_required = true ;
randomize_layout_attr . handler = handle_randomize_layout_attr ;
2017-05-06 09:37:45 +03:00
# if BUILDING_GCC_VERSION >= 4007
2018-02-06 04:27:46 +03:00
randomize_layout_attr . affects_type_identity = true ;
2017-05-06 09:37:45 +03:00
# endif
2018-02-06 04:27:46 +03:00
no_randomize_layout_attr . name = " no_randomize_layout " ;
no_randomize_layout_attr . type_required = true ;
no_randomize_layout_attr . handler = handle_randomize_layout_attr ;
2017-05-06 09:37:45 +03:00
# if BUILDING_GCC_VERSION >= 4007
2018-02-06 04:27:46 +03:00
no_randomize_layout_attr . affects_type_identity = true ;
2017-05-06 09:37:45 +03:00
# endif
2018-02-06 04:27:46 +03:00
randomize_considered_attr . name = " randomize_considered " ;
randomize_considered_attr . type_required = true ;
randomize_considered_attr . handler = handle_randomize_considered_attr ;
randomize_performed_attr . name = " randomize_performed " ;
randomize_performed_attr . type_required = true ;
randomize_performed_attr . handler = handle_randomize_performed_attr ;
2017-05-06 09:37:45 +03:00
register_attribute ( & randomize_layout_attr ) ;
register_attribute ( & no_randomize_layout_attr ) ;
register_attribute ( & randomize_considered_attr ) ;
register_attribute ( & randomize_performed_attr ) ;
}
static void check_bad_casts_in_constructor ( tree var , tree init )
{
unsigned HOST_WIDE_INT idx ;
tree field , val ;
tree field_type , val_type ;
FOR_EACH_CONSTRUCTOR_ELT ( CONSTRUCTOR_ELTS ( init ) , idx , field , val ) {
if ( TREE_CODE ( val ) = = CONSTRUCTOR ) {
check_bad_casts_in_constructor ( var , val ) ;
continue ;
}
/* pipacs' plugin creates franken-arrays that differ from those produced by
normal code which all have valid ' field ' trees . work around this */
if ( field = = NULL_TREE )
continue ;
field_type = TREE_TYPE ( field ) ;
val_type = TREE_TYPE ( val ) ;
if ( TREE_CODE ( field_type ) ! = POINTER_TYPE | | TREE_CODE ( val_type ) ! = POINTER_TYPE )
continue ;
if ( field_type = = val_type )
continue ;
field_type = TYPE_MAIN_VARIANT ( strip_array_types ( TYPE_MAIN_VARIANT ( TREE_TYPE ( field_type ) ) ) ) ;
val_type = TYPE_MAIN_VARIANT ( strip_array_types ( TYPE_MAIN_VARIANT ( TREE_TYPE ( val_type ) ) ) ) ;
if ( field_type = = void_type_node )
continue ;
if ( field_type = = val_type )
continue ;
if ( TREE_CODE ( val_type ) ! = RECORD_TYPE )
continue ;
if ( ! lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( val_type ) ) )
continue ;
MISMATCH ( DECL_SOURCE_LOCATION ( var ) , " constructor \n " , TYPE_MAIN_VARIANT ( field_type ) , TYPE_MAIN_VARIANT ( val_type ) ) ;
}
}
/* derived from the constify plugin */
static void check_global_variables ( void * event_data , void * data )
{
struct varpool_node * node ;
tree init ;
FOR_EACH_VARIABLE ( node ) {
tree var = NODE_DECL ( node ) ;
init = DECL_INITIAL ( var ) ;
if ( init = = NULL_TREE )
continue ;
if ( TREE_CODE ( init ) ! = CONSTRUCTOR )
continue ;
check_bad_casts_in_constructor ( var , init ) ;
}
}
static bool dominated_by_is_err ( const_tree rhs , basic_block bb )
{
basic_block dom ;
gimple dom_stmt ;
gimple call_stmt ;
const_tree dom_lhs ;
const_tree poss_is_err_cond ;
const_tree poss_is_err_func ;
const_tree is_err_arg ;
dom = get_immediate_dominator ( CDI_DOMINATORS , bb ) ;
if ( ! dom )
return false ;
dom_stmt = last_stmt ( dom ) ;
if ( ! dom_stmt )
return false ;
if ( gimple_code ( dom_stmt ) ! = GIMPLE_COND )
return false ;
if ( gimple_cond_code ( dom_stmt ) ! = NE_EXPR )
return false ;
if ( ! integer_zerop ( gimple_cond_rhs ( dom_stmt ) ) )
return false ;
poss_is_err_cond = gimple_cond_lhs ( dom_stmt ) ;
if ( TREE_CODE ( poss_is_err_cond ) ! = SSA_NAME )
return false ;
call_stmt = SSA_NAME_DEF_STMT ( poss_is_err_cond ) ;
if ( gimple_code ( call_stmt ) ! = GIMPLE_CALL )
return false ;
dom_lhs = gimple_get_lhs ( call_stmt ) ;
poss_is_err_func = gimple_call_fndecl ( call_stmt ) ;
if ( ! poss_is_err_func )
return false ;
if ( dom_lhs ! = poss_is_err_cond )
return false ;
if ( strcmp ( DECL_NAME_POINTER ( poss_is_err_func ) , " IS_ERR " ) )
return false ;
is_err_arg = gimple_call_arg ( call_stmt , 0 ) ;
if ( ! is_err_arg )
return false ;
if ( is_err_arg ! = rhs )
return false ;
return true ;
}
static void handle_local_var_initializers ( void )
{
tree var ;
unsigned int i ;
FOR_EACH_LOCAL_DECL ( cfun , i , var ) {
tree init = DECL_INITIAL ( var ) ;
if ( ! init )
continue ;
if ( TREE_CODE ( init ) ! = CONSTRUCTOR )
continue ;
check_bad_casts_in_constructor ( var , init ) ;
}
}
static bool type_name_eq ( gimple stmt , const_tree type_tree , const char * wanted_name )
{
const char * type_name ;
if ( type_tree = = NULL_TREE )
return false ;
switch ( TREE_CODE ( type_tree ) ) {
case RECORD_TYPE :
type_name = TYPE_NAME_POINTER ( type_tree ) ;
break ;
case INTEGER_TYPE :
if ( TYPE_PRECISION ( type_tree ) = = CHAR_TYPE_SIZE )
type_name = " char " ;
else {
INFORM ( gimple_location ( stmt ) , " found non-char INTEGER_TYPE cast comparison: %qT \n " , type_tree ) ;
debug_tree ( type_tree ) ;
return false ;
}
break ;
case POINTER_TYPE :
if ( TREE_CODE ( TREE_TYPE ( type_tree ) ) = = VOID_TYPE ) {
type_name = " void * " ;
break ;
} else {
INFORM ( gimple_location ( stmt ) , " found non-void POINTER_TYPE cast comparison %qT \n " , type_tree ) ;
debug_tree ( type_tree ) ;
return false ;
}
default :
INFORM ( gimple_location ( stmt ) , " unhandled cast comparison: %qT \n " , type_tree ) ;
debug_tree ( type_tree ) ;
return false ;
}
return strcmp ( type_name , wanted_name ) = = 0 ;
}
static bool whitelisted_cast ( gimple stmt , const_tree lhs_tree , const_tree rhs_tree )
{
const struct whitelist_entry * entry ;
expanded_location xloc = expand_location ( gimple_location ( stmt ) ) ;
for ( entry = whitelist ; entry - > pathname ; entry + + ) {
if ( ! strstr ( xloc . file , entry - > pathname ) )
continue ;
if ( type_name_eq ( stmt , lhs_tree , entry - > lhs ) & & type_name_eq ( stmt , rhs_tree , entry - > rhs ) )
return true ;
}
return false ;
}
/*
* iterate over all statements to find " bad " casts :
* those where the address of the start of a structure is cast
* to a pointer of a structure of a different type , or a
* structure pointer type is cast to a different structure pointer type
*/
static unsigned int find_bad_casts_execute ( void )
{
basic_block bb ;
handle_local_var_initializers ( ) ;
FOR_EACH_BB_FN ( bb , cfun ) {
gimple_stmt_iterator gsi ;
for ( gsi = gsi_start_bb ( bb ) ; ! gsi_end_p ( gsi ) ; gsi_next ( & gsi ) ) {
gimple stmt ;
const_tree lhs ;
const_tree lhs_type ;
const_tree rhs1 ;
const_tree rhs_type ;
const_tree ptr_lhs_type ;
const_tree ptr_rhs_type ;
const_tree op0 ;
const_tree op0_type ;
enum tree_code rhs_code ;
stmt = gsi_stmt ( gsi ) ;
# ifdef __DEBUG_PLUGIN
# ifdef __DEBUG_VERBOSE
debug_gimple_stmt ( stmt ) ;
debug_tree ( gimple_get_lhs ( stmt ) ) ;
# endif
# endif
if ( gimple_code ( stmt ) ! = GIMPLE_ASSIGN )
continue ;
# ifdef __DEBUG_PLUGIN
# ifdef __DEBUG_VERBOSE
debug_tree ( gimple_assign_rhs1 ( stmt ) ) ;
# endif
# endif
rhs_code = gimple_assign_rhs_code ( stmt ) ;
if ( rhs_code ! = ADDR_EXPR & & rhs_code ! = SSA_NAME )
continue ;
lhs = gimple_get_lhs ( stmt ) ;
lhs_type = TREE_TYPE ( lhs ) ;
rhs1 = gimple_assign_rhs1 ( stmt ) ;
rhs_type = TREE_TYPE ( rhs1 ) ;
if ( TREE_CODE ( rhs_type ) ! = POINTER_TYPE | |
TREE_CODE ( lhs_type ) ! = POINTER_TYPE )
continue ;
ptr_lhs_type = TYPE_MAIN_VARIANT ( strip_array_types ( TYPE_MAIN_VARIANT ( TREE_TYPE ( lhs_type ) ) ) ) ;
ptr_rhs_type = TYPE_MAIN_VARIANT ( strip_array_types ( TYPE_MAIN_VARIANT ( TREE_TYPE ( rhs_type ) ) ) ) ;
if ( ptr_rhs_type = = void_type_node )
continue ;
if ( ptr_lhs_type = = void_type_node )
continue ;
if ( dominated_by_is_err ( rhs1 , bb ) )
continue ;
if ( TREE_CODE ( ptr_rhs_type ) ! = RECORD_TYPE ) {
# ifndef __DEBUG_PLUGIN
if ( lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( ptr_lhs_type ) ) )
# endif
{
if ( ! whitelisted_cast ( stmt , ptr_lhs_type , ptr_rhs_type ) )
MISMATCH ( gimple_location ( stmt ) , " rhs " , ptr_lhs_type , ptr_rhs_type ) ;
}
continue ;
}
if ( rhs_code = = SSA_NAME & & ptr_lhs_type = = ptr_rhs_type )
continue ;
if ( rhs_code = = ADDR_EXPR ) {
op0 = TREE_OPERAND ( rhs1 , 0 ) ;
if ( op0 = = NULL_TREE )
continue ;
if ( TREE_CODE ( op0 ) ! = VAR_DECL )
continue ;
op0_type = TYPE_MAIN_VARIANT ( strip_array_types ( TYPE_MAIN_VARIANT ( TREE_TYPE ( op0 ) ) ) ) ;
if ( op0_type = = ptr_lhs_type )
continue ;
# ifndef __DEBUG_PLUGIN
if ( lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( op0_type ) ) )
# endif
{
if ( ! whitelisted_cast ( stmt , ptr_lhs_type , op0_type ) )
MISMATCH ( gimple_location ( stmt ) , " op0 " , ptr_lhs_type , op0_type ) ;
}
} else {
const_tree ssa_name_var = SSA_NAME_VAR ( rhs1 ) ;
/* skip bogus type casts introduced by container_of */
if ( ssa_name_var ! = NULL_TREE & & DECL_NAME ( ssa_name_var ) & &
! strcmp ( ( const char * ) DECL_NAME_POINTER ( ssa_name_var ) , " __mptr " ) )
continue ;
# ifndef __DEBUG_PLUGIN
if ( lookup_attribute ( " randomize_performed " , TYPE_ATTRIBUTES ( ptr_rhs_type ) ) )
# endif
{
if ( ! whitelisted_cast ( stmt , ptr_lhs_type , ptr_rhs_type ) )
MISMATCH ( gimple_location ( stmt ) , " ssa " , ptr_lhs_type , ptr_rhs_type ) ;
}
}
}
}
return 0 ;
}
# define PASS_NAME find_bad_casts
# define NO_GATE
# define TODO_FLAGS_FINISH TODO_dump_func
# include "gcc-generate-gimple-pass.h"
__visible int plugin_init ( struct plugin_name_args * plugin_info , struct plugin_gcc_version * version )
{
int i ;
const char * const plugin_name = plugin_info - > base_name ;
const int argc = plugin_info - > argc ;
const struct plugin_argument * const argv = plugin_info - > argv ;
bool enable = true ;
int obtained_seed = 0 ;
struct register_pass_info find_bad_casts_pass_info ;
find_bad_casts_pass_info . pass = make_find_bad_casts_pass ( ) ;
find_bad_casts_pass_info . reference_pass_name = " ssa " ;
find_bad_casts_pass_info . ref_pass_instance_number = 1 ;
find_bad_casts_pass_info . pos_op = PASS_POS_INSERT_AFTER ;
if ( ! plugin_default_version_check ( version , & gcc_version ) ) {
error ( G_ ( " incompatible gcc/plugin versions " ) ) ;
return 1 ;
}
if ( strncmp ( lang_hooks . name , " GNU C " , 5 ) & & ! strncmp ( lang_hooks . name , " GNU C+ " , 6 ) ) {
inform ( UNKNOWN_LOCATION , G_ ( " %s supports C only, not %s " ) , plugin_name , lang_hooks . name ) ;
enable = false ;
}
for ( i = 0 ; i < argc ; + + i ) {
if ( ! strcmp ( argv [ i ] . key , " disable " ) ) {
enable = false ;
continue ;
}
if ( ! strcmp ( argv [ i ] . key , " performance-mode " ) ) {
performance_mode = 1 ;
continue ;
}
error ( G_ ( " unknown option '-fplugin-arg-%s-%s' " ) , plugin_name , argv [ i ] . key ) ;
}
if ( strlen ( randstruct_seed ) ! = 64 ) {
error ( G_ ( " invalid seed value supplied for %s plugin " ) , plugin_name ) ;
return 1 ;
}
obtained_seed = sscanf ( randstruct_seed , " %016llx%016llx%016llx%016llx " ,
& shuffle_seed [ 0 ] , & shuffle_seed [ 1 ] , & shuffle_seed [ 2 ] , & shuffle_seed [ 3 ] ) ;
if ( obtained_seed ! = 4 ) {
error ( G_ ( " Invalid seed supplied for %s plugin " ) , plugin_name ) ;
return 1 ;
}
register_callback ( plugin_name , PLUGIN_INFO , NULL , & randomize_layout_plugin_info ) ;
if ( enable ) {
register_callback ( plugin_name , PLUGIN_ALL_IPA_PASSES_START , check_global_variables , NULL ) ;
register_callback ( plugin_name , PLUGIN_PASS_MANAGER_SETUP , NULL , & find_bad_casts_pass_info ) ;
register_callback ( plugin_name , PLUGIN_FINISH_TYPE , finish_type , NULL ) ;
register_callback ( plugin_name , PLUGIN_FINISH_DECL , randomize_layout_finish_decl , NULL ) ;
}
register_callback ( plugin_name , PLUGIN_ATTRIBUTES , register_attributes , NULL ) ;
return 0 ;
}