2017-01-13 22:14:39 +03:00
/*
* Copyright 2013 - 2017 by 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
*
* gcc plugin to forcibly initialize certain local variables that could
* otherwise leak kernel stack to userland if they aren ' t properly initialized
* by later code
*
* Homepage : http : //pax.grsecurity.net/
*
* Options :
* - fplugin - arg - structleak_plugin - disable
* - fplugin - arg - structleak_plugin - verbose
2017-08-06 14:06:27 +03:00
* - fplugin - arg - structleak_plugin - byref - all
2017-01-13 22:14:39 +03:00
*
* 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 structleak_plugin . so structleak_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 structleak_plugin . so structleak_plugin . c
* $ gcc - fplugin = . / structleak_plugin . so test . c - O2
*
* TODO : eliminate redundant initializers
* increase type coverage
*/
# include "gcc-common.h"
/* unused C type flag in all versions 4.5-6 */
# define TYPE_USERSPACE(TYPE) TYPE_LANG_FLAG_5(TYPE)
__visible int plugin_is_GPL_compatible ;
static struct plugin_info structleak_plugin_info = {
. version = " 201607271510vanilla " ,
. help = " disable \t do not activate plugin \n "
" verbose \t print all initialized variables \n " ,
} ;
static bool verbose ;
2017-08-06 14:06:27 +03:00
static bool byref_all ;
2017-01-13 22:14:39 +03:00
static tree handle_user_attribute ( tree * node , tree name , tree args , int flags , bool * no_add_attrs )
{
* no_add_attrs = true ;
/* check for types? for now accept everything linux has to offer */
if ( TREE_CODE ( * node ) ! = FIELD_DECL )
return NULL_TREE ;
* no_add_attrs = false ;
return NULL_TREE ;
}
2018-02-06 04:27:46 +03:00
static struct attribute_spec user_attr = { } ;
2017-01-13 22:14:39 +03:00
static void register_attributes ( void * event_data , void * data )
{
2018-02-06 04:27:46 +03:00
user_attr . name = " user " ;
user_attr . handler = handle_user_attribute ;
# if BUILDING_GCC_VERSION >= 4007
user_attr . affects_type_identity = true ;
# endif
2017-01-13 22:14:39 +03:00
register_attribute ( & user_attr ) ;
}
static tree get_field_type ( tree field )
{
return strip_array_types ( TREE_TYPE ( field ) ) ;
}
static bool is_userspace_type ( tree type )
{
tree field ;
for ( field = TYPE_FIELDS ( type ) ; field ; field = TREE_CHAIN ( field ) ) {
tree fieldtype = get_field_type ( field ) ;
enum tree_code code = TREE_CODE ( fieldtype ) ;
if ( code = = RECORD_TYPE | | code = = UNION_TYPE )
if ( is_userspace_type ( fieldtype ) )
return true ;
if ( lookup_attribute ( " user " , DECL_ATTRIBUTES ( field ) ) )
return true ;
}
return false ;
}
static void finish_type ( void * event_data , void * data )
{
tree type = ( tree ) event_data ;
if ( type = = NULL_TREE | | type = = error_mark_node )
return ;
# if BUILDING_GCC_VERSION >= 5000
if ( TREE_CODE ( type ) = = ENUMERAL_TYPE )
return ;
# endif
if ( TYPE_USERSPACE ( type ) )
return ;
if ( is_userspace_type ( type ) )
TYPE_USERSPACE ( type ) = 1 ;
}
static void initialize ( tree var )
{
basic_block bb ;
gimple_stmt_iterator gsi ;
tree initializer ;
gimple init_stmt ;
/* this is the original entry bb before the forced split */
bb = single_succ ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ;
/* first check if variable is already initialized, warn otherwise */
for ( gsi = gsi_start_bb ( bb ) ; ! gsi_end_p ( gsi ) ; gsi_next ( & gsi ) ) {
gimple stmt = gsi_stmt ( gsi ) ;
tree rhs1 ;
/* we're looking for an assignment of a single rhs... */
if ( ! gimple_assign_single_p ( stmt ) )
continue ;
rhs1 = gimple_assign_rhs1 ( stmt ) ;
# if BUILDING_GCC_VERSION >= 4007
/* ... of a non-clobbering expression... */
if ( TREE_CLOBBER_P ( rhs1 ) )
continue ;
# endif
/* ... to our variable... */
if ( gimple_get_lhs ( stmt ) ! = var )
continue ;
/* if it's an initializer then we're good */
if ( TREE_CODE ( rhs1 ) = = CONSTRUCTOR )
return ;
}
/* these aren't the 0days you're looking for */
if ( verbose )
inform ( DECL_SOURCE_LOCATION ( var ) ,
2017-08-06 14:06:27 +03:00
" %s variable will be forcibly initialized " ,
( byref_all & & TREE_ADDRESSABLE ( var ) ) ? " byref "
: " userspace " ) ;
2017-01-13 22:14:39 +03:00
/* build the initializer expression */
initializer = build_constructor ( TREE_TYPE ( var ) , NULL ) ;
/* build the initializer stmt */
init_stmt = gimple_build_assign ( var , initializer ) ;
gsi = gsi_after_labels ( single_succ ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ) ;
gsi_insert_before ( & gsi , init_stmt , GSI_NEW_STMT ) ;
update_stmt ( init_stmt ) ;
}
static unsigned int structleak_execute ( void )
{
basic_block bb ;
unsigned int ret = 0 ;
tree var ;
unsigned int i ;
/* split the first bb where we can put the forced initializers */
gcc_assert ( single_succ_p ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ) ;
bb = single_succ ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ;
if ( ! single_pred_p ( bb ) ) {
split_edge ( single_succ_edge ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ) ;
gcc_assert ( single_succ_p ( ENTRY_BLOCK_PTR_FOR_FN ( cfun ) ) ) ;
}
/* enumerate all local variables and forcibly initialize our targets */
FOR_EACH_LOCAL_DECL ( cfun , i , var ) {
tree type = TREE_TYPE ( var ) ;
gcc_assert ( DECL_P ( var ) ) ;
if ( ! auto_var_in_fn_p ( var , current_function_decl ) )
continue ;
/* only care about structure types */
if ( TREE_CODE ( type ) ! = RECORD_TYPE & & TREE_CODE ( type ) ! = UNION_TYPE )
continue ;
/* if the type is of interest, examine the variable */
2017-08-06 14:06:27 +03:00
if ( TYPE_USERSPACE ( type ) | |
( byref_all & & TREE_ADDRESSABLE ( var ) ) )
2017-01-13 22:14:39 +03:00
initialize ( var ) ;
}
return ret ;
}
# define PASS_NAME structleak
# define NO_GATE
# define PROPERTIES_REQUIRED PROP_cfg
# define TODO_FLAGS_FINISH TODO_verify_il | TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_ggc_collect | TODO_verify_flow
# 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 ;
PASS_INFO ( structleak , " early_optimizations " , 1 , PASS_POS_INSERT_BEFORE ) ;
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 , " verbose " ) ) {
verbose = true ;
continue ;
}
2017-08-06 14:06:27 +03:00
if ( ! strcmp ( argv [ i ] . key , " byref-all " ) ) {
byref_all = true ;
continue ;
}
2017-01-13 22:14:39 +03:00
error ( G_ ( " unknown option '-fplugin-arg-%s-%s' " ) , plugin_name , argv [ i ] . key ) ;
}
register_callback ( plugin_name , PLUGIN_INFO , NULL , & structleak_plugin_info ) ;
if ( enable ) {
register_callback ( plugin_name , PLUGIN_PASS_MANAGER_SETUP , NULL , & structleak_pass_info ) ;
register_callback ( plugin_name , PLUGIN_FINISH_TYPE , finish_type , NULL ) ;
}
register_callback ( plugin_name , PLUGIN_ATTRIBUTES , register_attributes , NULL ) ;
return 0 ;
}