Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2012-2016 by the PaX Team <pageexec@freemail.hu>
0004  * Copyright 2016 by Emese Revfy <re.emese@gmail.com>
0005  *
0006  * Note: the choice of the license means that the compilation process is
0007  *       NOT 'eligible' as defined by gcc's library exception to the GPL v3,
0008  *       but for the kernel it doesn't matter since it doesn't link against
0009  *       any of the gcc libraries
0010  *
0011  * This gcc plugin helps generate a little bit of entropy from program state,
0012  * used throughout the uptime of the kernel. Here is an instrumentation example:
0013  *
0014  * before:
0015  * void __latent_entropy test(int argc, char *argv[])
0016  * {
0017  *  if (argc <= 1)
0018  *      printf("%s: no command arguments :(\n", *argv);
0019  *  else
0020  *      printf("%s: %d command arguments!\n", *argv, args - 1);
0021  * }
0022  *
0023  * after:
0024  * void __latent_entropy test(int argc, char *argv[])
0025  * {
0026  *  // latent_entropy_execute() 1.
0027  *  unsigned long local_entropy;
0028  *  // init_local_entropy() 1.
0029  *  void *local_entropy_frameaddr;
0030  *  // init_local_entropy() 3.
0031  *  unsigned long tmp_latent_entropy;
0032  *
0033  *  // init_local_entropy() 2.
0034  *  local_entropy_frameaddr = __builtin_frame_address(0);
0035  *  local_entropy = (unsigned long) local_entropy_frameaddr;
0036  *
0037  *  // init_local_entropy() 4.
0038  *  tmp_latent_entropy = latent_entropy;
0039  *  // init_local_entropy() 5.
0040  *  local_entropy ^= tmp_latent_entropy;
0041  *
0042  *  // latent_entropy_execute() 3.
0043  *  if (argc <= 1) {
0044  *      // perturb_local_entropy()
0045  *      local_entropy += 4623067384293424948;
0046  *      printf("%s: no command arguments :(\n", *argv);
0047  *      // perturb_local_entropy()
0048  *  } else {
0049  *      local_entropy ^= 3896280633962944730;
0050  *      printf("%s: %d command arguments!\n", *argv, args - 1);
0051  *  }
0052  *
0053  *  // latent_entropy_execute() 4.
0054  *  tmp_latent_entropy = rol(tmp_latent_entropy, local_entropy);
0055  *  latent_entropy = tmp_latent_entropy;
0056  * }
0057  *
0058  * TODO:
0059  * - add ipa pass to identify not explicitly marked candidate functions
0060  * - mix in more program state (function arguments/return values,
0061  *   loop variables, etc)
0062  * - more instrumentation control via attribute parameters
0063  *
0064  * BUGS:
0065  * - none known
0066  *
0067  * Options:
0068  * -fplugin-arg-latent_entropy_plugin-disable
0069  *
0070  * Attribute: __attribute__((latent_entropy))
0071  *  The latent_entropy gcc attribute can be only on functions and variables.
0072  *  If it is on a function then the plugin will instrument it. If the attribute
0073  *  is on a variable then the plugin will initialize it with a random value.
0074  *  The variable must be an integer, an integer array type or a structure
0075  *  with integer fields.
0076  */
0077 
0078 #include "gcc-common.h"
0079 
0080 __visible int plugin_is_GPL_compatible;
0081 
0082 static GTY(()) tree latent_entropy_decl;
0083 
0084 static struct plugin_info latent_entropy_plugin_info = {
0085     .version    = PLUGIN_VERSION,
0086     .help       = "disable\tturn off latent entropy instrumentation\n",
0087 };
0088 
0089 static unsigned HOST_WIDE_INT deterministic_seed;
0090 static unsigned HOST_WIDE_INT rnd_buf[32];
0091 static size_t rnd_idx = ARRAY_SIZE(rnd_buf);
0092 static int urandom_fd = -1;
0093 
0094 static unsigned HOST_WIDE_INT get_random_const(void)
0095 {
0096     if (deterministic_seed) {
0097         unsigned HOST_WIDE_INT w = deterministic_seed;
0098         w ^= w << 13;
0099         w ^= w >> 7;
0100         w ^= w << 17;
0101         deterministic_seed = w;
0102         return deterministic_seed;
0103     }
0104 
0105     if (urandom_fd < 0) {
0106         urandom_fd = open("/dev/urandom", O_RDONLY);
0107         gcc_assert(urandom_fd >= 0);
0108     }
0109     if (rnd_idx >= ARRAY_SIZE(rnd_buf)) {
0110         gcc_assert(read(urandom_fd, rnd_buf, sizeof(rnd_buf)) == sizeof(rnd_buf));
0111         rnd_idx = 0;
0112     }
0113     return rnd_buf[rnd_idx++];
0114 }
0115 
0116 static tree tree_get_random_const(tree type)
0117 {
0118     unsigned long long mask;
0119 
0120     mask = 1ULL << (TREE_INT_CST_LOW(TYPE_SIZE(type)) - 1);
0121     mask = 2 * (mask - 1) + 1;
0122 
0123     if (TYPE_UNSIGNED(type))
0124         return build_int_cstu(type, mask & get_random_const());
0125     return build_int_cst(type, mask & get_random_const());
0126 }
0127 
0128 static tree handle_latent_entropy_attribute(tree *node, tree name,
0129                         tree args __unused,
0130                         int flags __unused,
0131                         bool *no_add_attrs)
0132 {
0133     tree type;
0134     vec<constructor_elt, va_gc> *vals;
0135 
0136     switch (TREE_CODE(*node)) {
0137     default:
0138         *no_add_attrs = true;
0139         error("%qE attribute only applies to functions and variables",
0140             name);
0141         break;
0142 
0143     case VAR_DECL:
0144         if (DECL_INITIAL(*node)) {
0145             *no_add_attrs = true;
0146             error("variable %qD with %qE attribute must not be initialized",
0147                 *node, name);
0148             break;
0149         }
0150 
0151         if (!TREE_STATIC(*node)) {
0152             *no_add_attrs = true;
0153             error("variable %qD with %qE attribute must not be local",
0154                 *node, name);
0155             break;
0156         }
0157 
0158         type = TREE_TYPE(*node);
0159         switch (TREE_CODE(type)) {
0160         default:
0161             *no_add_attrs = true;
0162             error("variable %qD with %qE attribute must be an integer or a fixed length integer array type or a fixed sized structure with integer fields",
0163                 *node, name);
0164             break;
0165 
0166         case RECORD_TYPE: {
0167             tree fld, lst = TYPE_FIELDS(type);
0168             unsigned int nelt = 0;
0169 
0170             for (fld = lst; fld; nelt++, fld = TREE_CHAIN(fld)) {
0171                 tree fieldtype;
0172 
0173                 fieldtype = TREE_TYPE(fld);
0174                 if (TREE_CODE(fieldtype) == INTEGER_TYPE)
0175                     continue;
0176 
0177                 *no_add_attrs = true;
0178                 error("structure variable %qD with %qE attribute has a non-integer field %qE",
0179                     *node, name, fld);
0180                 break;
0181             }
0182 
0183             if (fld)
0184                 break;
0185 
0186             vec_alloc(vals, nelt);
0187 
0188             for (fld = lst; fld; fld = TREE_CHAIN(fld)) {
0189                 tree random_const, fld_t = TREE_TYPE(fld);
0190 
0191                 random_const = tree_get_random_const(fld_t);
0192                 CONSTRUCTOR_APPEND_ELT(vals, fld, random_const);
0193             }
0194 
0195             /* Initialize the fields with random constants */
0196             DECL_INITIAL(*node) = build_constructor(type, vals);
0197             break;
0198         }
0199 
0200         /* Initialize the variable with a random constant */
0201         case INTEGER_TYPE:
0202             DECL_INITIAL(*node) = tree_get_random_const(type);
0203             break;
0204 
0205         case ARRAY_TYPE: {
0206             tree elt_type, array_size, elt_size;
0207             unsigned int i, nelt;
0208             HOST_WIDE_INT array_size_int, elt_size_int;
0209 
0210             elt_type = TREE_TYPE(type);
0211             elt_size = TYPE_SIZE_UNIT(TREE_TYPE(type));
0212             array_size = TYPE_SIZE_UNIT(type);
0213 
0214             if (TREE_CODE(elt_type) != INTEGER_TYPE || !array_size
0215                 || TREE_CODE(array_size) != INTEGER_CST) {
0216                 *no_add_attrs = true;
0217                 error("array variable %qD with %qE attribute must be a fixed length integer array type",
0218                     *node, name);
0219                 break;
0220             }
0221 
0222             array_size_int = TREE_INT_CST_LOW(array_size);
0223             elt_size_int = TREE_INT_CST_LOW(elt_size);
0224             nelt = array_size_int / elt_size_int;
0225 
0226             vec_alloc(vals, nelt);
0227 
0228             for (i = 0; i < nelt; i++) {
0229                 tree cst = size_int(i);
0230                 tree rand_cst = tree_get_random_const(elt_type);
0231 
0232                 CONSTRUCTOR_APPEND_ELT(vals, cst, rand_cst);
0233             }
0234 
0235             /*
0236              * Initialize the elements of the array with random
0237              * constants
0238              */
0239             DECL_INITIAL(*node) = build_constructor(type, vals);
0240             break;
0241         }
0242         }
0243         break;
0244 
0245     case FUNCTION_DECL:
0246         break;
0247     }
0248 
0249     return NULL_TREE;
0250 }
0251 
0252 static struct attribute_spec latent_entropy_attr = { };
0253 
0254 static void register_attributes(void *event_data __unused, void *data __unused)
0255 {
0256     latent_entropy_attr.name        = "latent_entropy";
0257     latent_entropy_attr.decl_required   = true;
0258     latent_entropy_attr.handler     = handle_latent_entropy_attribute;
0259 
0260     register_attribute(&latent_entropy_attr);
0261 }
0262 
0263 static bool latent_entropy_gate(void)
0264 {
0265     tree list;
0266 
0267     /* don't bother with noreturn functions for now */
0268     if (TREE_THIS_VOLATILE(current_function_decl))
0269         return false;
0270 
0271     /* gcc-4.5 doesn't discover some trivial noreturn functions */
0272     if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0)
0273         return false;
0274 
0275     list = DECL_ATTRIBUTES(current_function_decl);
0276     return lookup_attribute("latent_entropy", list) != NULL_TREE;
0277 }
0278 
0279 static tree create_var(tree type, const char *name)
0280 {
0281     tree var;
0282 
0283     var = create_tmp_var(type, name);
0284     add_referenced_var(var);
0285     mark_sym_for_renaming(var);
0286     return var;
0287 }
0288 
0289 /*
0290  * Set up the next operation and its constant operand to use in the latent
0291  * entropy PRNG. When RHS is specified, the request is for perturbing the
0292  * local latent entropy variable, otherwise it is for perturbing the global
0293  * latent entropy variable where the two operands are already given by the
0294  * local and global latent entropy variables themselves.
0295  *
0296  * The operation is one of add/xor/rol when instrumenting the local entropy
0297  * variable and one of add/xor when perturbing the global entropy variable.
0298  * Rotation is not used for the latter case because it would transmit less
0299  * entropy to the global variable than the other two operations.
0300  */
0301 static enum tree_code get_op(tree *rhs)
0302 {
0303     static enum tree_code op;
0304     unsigned HOST_WIDE_INT random_const;
0305 
0306     random_const = get_random_const();
0307 
0308     switch (op) {
0309     case BIT_XOR_EXPR:
0310         op = PLUS_EXPR;
0311         break;
0312 
0313     case PLUS_EXPR:
0314         if (rhs) {
0315             op = LROTATE_EXPR;
0316             /*
0317              * This code limits the value of random_const to
0318              * the size of a long for the rotation
0319              */
0320             random_const %= TYPE_PRECISION(long_unsigned_type_node);
0321             break;
0322         }
0323 
0324     case LROTATE_EXPR:
0325     default:
0326         op = BIT_XOR_EXPR;
0327         break;
0328     }
0329     if (rhs)
0330         *rhs = build_int_cstu(long_unsigned_type_node, random_const);
0331     return op;
0332 }
0333 
0334 static gimple create_assign(enum tree_code code, tree lhs, tree op1,
0335                 tree op2)
0336 {
0337     return gimple_build_assign_with_ops(code, lhs, op1, op2);
0338 }
0339 
0340 static void perturb_local_entropy(basic_block bb, tree local_entropy)
0341 {
0342     gimple_stmt_iterator gsi;
0343     gimple assign;
0344     tree rhs;
0345     enum tree_code op;
0346 
0347     op = get_op(&rhs);
0348     assign = create_assign(op, local_entropy, local_entropy, rhs);
0349     gsi = gsi_after_labels(bb);
0350     gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
0351     update_stmt(assign);
0352 }
0353 
0354 static void __perturb_latent_entropy(gimple_stmt_iterator *gsi,
0355                     tree local_entropy)
0356 {
0357     gimple assign;
0358     tree temp;
0359     enum tree_code op;
0360 
0361     /* 1. create temporary copy of latent_entropy */
0362     temp = create_var(long_unsigned_type_node, "temp_latent_entropy");
0363 
0364     /* 2. read... */
0365     add_referenced_var(latent_entropy_decl);
0366     mark_sym_for_renaming(latent_entropy_decl);
0367     assign = gimple_build_assign(temp, latent_entropy_decl);
0368     gsi_insert_before(gsi, assign, GSI_NEW_STMT);
0369     update_stmt(assign);
0370 
0371     /* 3. ...modify... */
0372     op = get_op(NULL);
0373     assign = create_assign(op, temp, temp, local_entropy);
0374     gsi_insert_after(gsi, assign, GSI_NEW_STMT);
0375     update_stmt(assign);
0376 
0377     /* 4. ...write latent_entropy */
0378     assign = gimple_build_assign(latent_entropy_decl, temp);
0379     gsi_insert_after(gsi, assign, GSI_NEW_STMT);
0380     update_stmt(assign);
0381 }
0382 
0383 static bool handle_tail_calls(basic_block bb, tree local_entropy)
0384 {
0385     gimple_stmt_iterator gsi;
0386 
0387     for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
0388         gcall *call;
0389         gimple stmt = gsi_stmt(gsi);
0390 
0391         if (!is_gimple_call(stmt))
0392             continue;
0393 
0394         call = as_a_gcall(stmt);
0395         if (!gimple_call_tail_p(call))
0396             continue;
0397 
0398         __perturb_latent_entropy(&gsi, local_entropy);
0399         return true;
0400     }
0401 
0402     return false;
0403 }
0404 
0405 static void perturb_latent_entropy(tree local_entropy)
0406 {
0407     edge_iterator ei;
0408     edge e, last_bb_e;
0409     basic_block last_bb;
0410 
0411     gcc_assert(single_pred_p(EXIT_BLOCK_PTR_FOR_FN(cfun)));
0412     last_bb_e = single_pred_edge(EXIT_BLOCK_PTR_FOR_FN(cfun));
0413 
0414     FOR_EACH_EDGE(e, ei, last_bb_e->src->preds) {
0415         if (ENTRY_BLOCK_PTR_FOR_FN(cfun) == e->src)
0416             continue;
0417         if (EXIT_BLOCK_PTR_FOR_FN(cfun) == e->src)
0418             continue;
0419 
0420         handle_tail_calls(e->src, local_entropy);
0421     }
0422 
0423     last_bb = single_pred(EXIT_BLOCK_PTR_FOR_FN(cfun));
0424     if (!handle_tail_calls(last_bb, local_entropy)) {
0425         gimple_stmt_iterator gsi = gsi_last_bb(last_bb);
0426 
0427         __perturb_latent_entropy(&gsi, local_entropy);
0428     }
0429 }
0430 
0431 static void init_local_entropy(basic_block bb, tree local_entropy)
0432 {
0433     gimple assign, call;
0434     tree frame_addr, rand_const, tmp, fndecl, udi_frame_addr;
0435     enum tree_code op;
0436     unsigned HOST_WIDE_INT rand_cst;
0437     gimple_stmt_iterator gsi = gsi_after_labels(bb);
0438 
0439     /* 1. create local_entropy_frameaddr */
0440     frame_addr = create_var(ptr_type_node, "local_entropy_frameaddr");
0441 
0442     /* 2. local_entropy_frameaddr = __builtin_frame_address() */
0443     fndecl = builtin_decl_implicit(BUILT_IN_FRAME_ADDRESS);
0444     call = gimple_build_call(fndecl, 1, integer_zero_node);
0445     gimple_call_set_lhs(call, frame_addr);
0446     gsi_insert_before(&gsi, call, GSI_NEW_STMT);
0447     update_stmt(call);
0448 
0449     udi_frame_addr = fold_convert(long_unsigned_type_node, frame_addr);
0450     assign = gimple_build_assign(local_entropy, udi_frame_addr);
0451     gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
0452     update_stmt(assign);
0453 
0454     /* 3. create temporary copy of latent_entropy */
0455     tmp = create_var(long_unsigned_type_node, "temp_latent_entropy");
0456 
0457     /* 4. read the global entropy variable into local entropy */
0458     add_referenced_var(latent_entropy_decl);
0459     mark_sym_for_renaming(latent_entropy_decl);
0460     assign = gimple_build_assign(tmp, latent_entropy_decl);
0461     gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
0462     update_stmt(assign);
0463 
0464     /* 5. mix local_entropy_frameaddr into local entropy */
0465     assign = create_assign(BIT_XOR_EXPR, local_entropy, local_entropy, tmp);
0466     gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
0467     update_stmt(assign);
0468 
0469     rand_cst = get_random_const();
0470     rand_const = build_int_cstu(long_unsigned_type_node, rand_cst);
0471     op = get_op(NULL);
0472     assign = create_assign(op, local_entropy, local_entropy, rand_const);
0473     gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
0474     update_stmt(assign);
0475 }
0476 
0477 static bool create_latent_entropy_decl(void)
0478 {
0479     varpool_node_ptr node;
0480 
0481     if (latent_entropy_decl != NULL_TREE)
0482         return true;
0483 
0484     FOR_EACH_VARIABLE(node) {
0485         tree name, var = NODE_DECL(node);
0486 
0487         if (DECL_NAME_LENGTH(var) < sizeof("latent_entropy") - 1)
0488             continue;
0489 
0490         name = DECL_NAME(var);
0491         if (strcmp(IDENTIFIER_POINTER(name), "latent_entropy"))
0492             continue;
0493 
0494         latent_entropy_decl = var;
0495         break;
0496     }
0497 
0498     return latent_entropy_decl != NULL_TREE;
0499 }
0500 
0501 static unsigned int latent_entropy_execute(void)
0502 {
0503     basic_block bb;
0504     tree local_entropy;
0505 
0506     if (!create_latent_entropy_decl())
0507         return 0;
0508 
0509     /* prepare for step 2 below */
0510     gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
0511     bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
0512     if (!single_pred_p(bb)) {
0513         split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
0514         gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
0515         bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
0516     }
0517 
0518     /* 1. create the local entropy variable */
0519     local_entropy = create_var(long_unsigned_type_node, "local_entropy");
0520 
0521     /* 2. initialize the local entropy variable */
0522     init_local_entropy(bb, local_entropy);
0523 
0524     bb = bb->next_bb;
0525 
0526     /*
0527      * 3. instrument each BB with an operation on the
0528      *    local entropy variable
0529      */
0530     while (bb != EXIT_BLOCK_PTR_FOR_FN(cfun)) {
0531         perturb_local_entropy(bb, local_entropy);
0532         bb = bb->next_bb;
0533     }
0534 
0535     /* 4. mix local entropy into the global entropy variable */
0536     perturb_latent_entropy(local_entropy);
0537     return 0;
0538 }
0539 
0540 static void latent_entropy_start_unit(void *gcc_data __unused,
0541                     void *user_data __unused)
0542 {
0543     tree type, id;
0544     int quals;
0545 
0546     if (in_lto_p)
0547         return;
0548 
0549     /* extern volatile unsigned long latent_entropy */
0550     quals = TYPE_QUALS(long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
0551     type = build_qualified_type(long_unsigned_type_node, quals);
0552     id = get_identifier("latent_entropy");
0553     latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
0554 
0555     TREE_STATIC(latent_entropy_decl) = 1;
0556     TREE_PUBLIC(latent_entropy_decl) = 1;
0557     TREE_USED(latent_entropy_decl) = 1;
0558     DECL_PRESERVE_P(latent_entropy_decl) = 1;
0559     TREE_THIS_VOLATILE(latent_entropy_decl) = 1;
0560     DECL_EXTERNAL(latent_entropy_decl) = 1;
0561     DECL_ARTIFICIAL(latent_entropy_decl) = 1;
0562     lang_hooks.decls.pushdecl(latent_entropy_decl);
0563 }
0564 
0565 #define PASS_NAME latent_entropy
0566 #define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
0567 #define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
0568     | TODO_update_ssa
0569 #include "gcc-generate-gimple-pass.h"
0570 
0571 __visible int plugin_init(struct plugin_name_args *plugin_info,
0572               struct plugin_gcc_version *version)
0573 {
0574     bool enabled = true;
0575     const char * const plugin_name = plugin_info->base_name;
0576     const int argc = plugin_info->argc;
0577     const struct plugin_argument * const argv = plugin_info->argv;
0578     int i;
0579 
0580     /*
0581      * Call get_random_seed() with noinit=true, so that this returns
0582      * 0 in the case where no seed has been passed via -frandom-seed.
0583      */
0584     deterministic_seed = get_random_seed(true);
0585 
0586     static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
0587         {
0588             .base = &latent_entropy_decl,
0589             .nelt = 1,
0590             .stride = sizeof(latent_entropy_decl),
0591             .cb = &gt_ggc_mx_tree_node,
0592             .pchw = &gt_pch_nx_tree_node
0593         },
0594         LAST_GGC_ROOT_TAB
0595     };
0596 
0597     PASS_INFO(latent_entropy, "optimized", 1, PASS_POS_INSERT_BEFORE);
0598 
0599     if (!plugin_default_version_check(version, &gcc_version)) {
0600         error(G_("incompatible gcc/plugin versions"));
0601         return 1;
0602     }
0603 
0604     for (i = 0; i < argc; ++i) {
0605         if (!(strcmp(argv[i].key, "disable"))) {
0606             enabled = false;
0607             continue;
0608         }
0609         error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
0610     }
0611 
0612     register_callback(plugin_name, PLUGIN_INFO, NULL,
0613                 &latent_entropy_plugin_info);
0614     if (enabled) {
0615         register_callback(plugin_name, PLUGIN_START_UNIT,
0616                     &latent_entropy_start_unit, NULL);
0617         register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
0618                   NULL, (void *)&gt_ggc_r_gt_latent_entropy);
0619         register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
0620                     &latent_entropy_pass_info);
0621     }
0622     register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
0623                 NULL);
0624 
0625     return 0;
0626 }