latent_entropy_plugin.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. /*
  2. * Copyright 2012-2016 by the PaX Team <[email protected]>
  3. * Copyright 2016 by Emese Revfy <[email protected]>
  4. * Licensed under the GPL v2
  5. *
  6. * Note: the choice of the license means that the compilation process is
  7. * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
  8. * but for the kernel it doesn't matter since it doesn't link against
  9. * any of the gcc libraries
  10. *
  11. * This gcc plugin helps generate a little bit of entropy from program state,
  12. * used throughout the uptime of the kernel. Here is an instrumentation example:
  13. *
  14. * before:
  15. * void __latent_entropy test(int argc, char *argv[])
  16. * {
  17. * if (argc <= 1)
  18. * printf("%s: no command arguments :(\n", *argv);
  19. * else
  20. * printf("%s: %d command arguments!\n", *argv, args - 1);
  21. * }
  22. *
  23. * after:
  24. * void __latent_entropy test(int argc, char *argv[])
  25. * {
  26. * // latent_entropy_execute() 1.
  27. * unsigned long local_entropy;
  28. * // init_local_entropy() 1.
  29. * void *local_entropy_frameaddr;
  30. * // init_local_entropy() 3.
  31. * unsigned long tmp_latent_entropy;
  32. *
  33. * // init_local_entropy() 2.
  34. * local_entropy_frameaddr = __builtin_frame_address(0);
  35. * local_entropy = (unsigned long) local_entropy_frameaddr;
  36. *
  37. * // init_local_entropy() 4.
  38. * tmp_latent_entropy = latent_entropy;
  39. * // init_local_entropy() 5.
  40. * local_entropy ^= tmp_latent_entropy;
  41. *
  42. * // latent_entropy_execute() 3.
  43. * if (argc <= 1) {
  44. * // perturb_local_entropy()
  45. * local_entropy += 4623067384293424948;
  46. * printf("%s: no command arguments :(\n", *argv);
  47. * // perturb_local_entropy()
  48. * } else {
  49. * local_entropy ^= 3896280633962944730;
  50. * printf("%s: %d command arguments!\n", *argv, args - 1);
  51. * }
  52. *
  53. * // latent_entropy_execute() 4.
  54. * tmp_latent_entropy = rol(tmp_latent_entropy, local_entropy);
  55. * latent_entropy = tmp_latent_entropy;
  56. * }
  57. *
  58. * TODO:
  59. * - add ipa pass to identify not explicitly marked candidate functions
  60. * - mix in more program state (function arguments/return values,
  61. * loop variables, etc)
  62. * - more instrumentation control via attribute parameters
  63. *
  64. * BUGS:
  65. * - none known
  66. *
  67. * Options:
  68. * -fplugin-arg-latent_entropy_plugin-disable
  69. *
  70. * Attribute: __attribute__((latent_entropy))
  71. * The latent_entropy gcc attribute can be only on functions and variables.
  72. * If it is on a function then the plugin will instrument it. If the attribute
  73. * is on a variable then the plugin will initialize it with a random value.
  74. * The variable must be an integer, an integer array type or a structure
  75. * with integer fields.
  76. */
  77. #include "gcc-common.h"
  78. __visible int plugin_is_GPL_compatible;
  79. static GTY(()) tree latent_entropy_decl;
  80. static struct plugin_info latent_entropy_plugin_info = {
  81. .version = "201606141920vanilla",
  82. .help = "disable\tturn off latent entropy instrumentation\n",
  83. };
  84. static unsigned HOST_WIDE_INT seed;
  85. /*
  86. * get_random_seed() (this is a GCC function) generates the seed.
  87. * This is a simple random generator without any cryptographic security because
  88. * the entropy doesn't come from here.
  89. */
  90. static unsigned HOST_WIDE_INT get_random_const(void)
  91. {
  92. unsigned int i;
  93. unsigned HOST_WIDE_INT ret = 0;
  94. for (i = 0; i < 8 * sizeof(ret); i++) {
  95. ret = (ret << 1) | (seed & 1);
  96. seed >>= 1;
  97. if (ret & 1)
  98. seed ^= 0xD800000000000000ULL;
  99. }
  100. return ret;
  101. }
  102. static tree tree_get_random_const(tree type)
  103. {
  104. unsigned long long mask;
  105. mask = 1ULL << (TREE_INT_CST_LOW(TYPE_SIZE(type)) - 1);
  106. mask = 2 * (mask - 1) + 1;
  107. if (TYPE_UNSIGNED(type))
  108. return build_int_cstu(type, mask & get_random_const());
  109. return build_int_cst(type, mask & get_random_const());
  110. }
  111. static tree handle_latent_entropy_attribute(tree *node, tree name,
  112. tree args __unused,
  113. int flags __unused,
  114. bool *no_add_attrs)
  115. {
  116. tree type;
  117. #if BUILDING_GCC_VERSION <= 4007
  118. VEC(constructor_elt, gc) *vals;
  119. #else
  120. vec<constructor_elt, va_gc> *vals;
  121. #endif
  122. switch (TREE_CODE(*node)) {
  123. default:
  124. *no_add_attrs = true;
  125. error("%qE attribute only applies to functions and variables",
  126. name);
  127. break;
  128. case VAR_DECL:
  129. if (DECL_INITIAL(*node)) {
  130. *no_add_attrs = true;
  131. error("variable %qD with %qE attribute must not be initialized",
  132. *node, name);
  133. break;
  134. }
  135. if (!TREE_STATIC(*node)) {
  136. *no_add_attrs = true;
  137. error("variable %qD with %qE attribute must not be local",
  138. *node, name);
  139. break;
  140. }
  141. type = TREE_TYPE(*node);
  142. switch (TREE_CODE(type)) {
  143. default:
  144. *no_add_attrs = true;
  145. 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",
  146. *node, name);
  147. break;
  148. case RECORD_TYPE: {
  149. tree fld, lst = TYPE_FIELDS(type);
  150. unsigned int nelt = 0;
  151. for (fld = lst; fld; nelt++, fld = TREE_CHAIN(fld)) {
  152. tree fieldtype;
  153. fieldtype = TREE_TYPE(fld);
  154. if (TREE_CODE(fieldtype) == INTEGER_TYPE)
  155. continue;
  156. *no_add_attrs = true;
  157. error("structure variable %qD with %qE attribute has a non-integer field %qE",
  158. *node, name, fld);
  159. break;
  160. }
  161. if (fld)
  162. break;
  163. #if BUILDING_GCC_VERSION <= 4007
  164. vals = VEC_alloc(constructor_elt, gc, nelt);
  165. #else
  166. vec_alloc(vals, nelt);
  167. #endif
  168. for (fld = lst; fld; fld = TREE_CHAIN(fld)) {
  169. tree random_const, fld_t = TREE_TYPE(fld);
  170. random_const = tree_get_random_const(fld_t);
  171. CONSTRUCTOR_APPEND_ELT(vals, fld, random_const);
  172. }
  173. /* Initialize the fields with random constants */
  174. DECL_INITIAL(*node) = build_constructor(type, vals);
  175. break;
  176. }
  177. /* Initialize the variable with a random constant */
  178. case INTEGER_TYPE:
  179. DECL_INITIAL(*node) = tree_get_random_const(type);
  180. break;
  181. case ARRAY_TYPE: {
  182. tree elt_type, array_size, elt_size;
  183. unsigned int i, nelt;
  184. HOST_WIDE_INT array_size_int, elt_size_int;
  185. elt_type = TREE_TYPE(type);
  186. elt_size = TYPE_SIZE_UNIT(TREE_TYPE(type));
  187. array_size = TYPE_SIZE_UNIT(type);
  188. if (TREE_CODE(elt_type) != INTEGER_TYPE || !array_size
  189. || TREE_CODE(array_size) != INTEGER_CST) {
  190. *no_add_attrs = true;
  191. error("array variable %qD with %qE attribute must be a fixed length integer array type",
  192. *node, name);
  193. break;
  194. }
  195. array_size_int = TREE_INT_CST_LOW(array_size);
  196. elt_size_int = TREE_INT_CST_LOW(elt_size);
  197. nelt = array_size_int / elt_size_int;
  198. #if BUILDING_GCC_VERSION <= 4007
  199. vals = VEC_alloc(constructor_elt, gc, nelt);
  200. #else
  201. vec_alloc(vals, nelt);
  202. #endif
  203. for (i = 0; i < nelt; i++) {
  204. tree cst = size_int(i);
  205. tree rand_cst = tree_get_random_const(elt_type);
  206. CONSTRUCTOR_APPEND_ELT(vals, cst, rand_cst);
  207. }
  208. /*
  209. * Initialize the elements of the array with random
  210. * constants
  211. */
  212. DECL_INITIAL(*node) = build_constructor(type, vals);
  213. break;
  214. }
  215. }
  216. break;
  217. case FUNCTION_DECL:
  218. break;
  219. }
  220. return NULL_TREE;
  221. }
  222. static struct attribute_spec latent_entropy_attr = {
  223. .name = "latent_entropy",
  224. .min_length = 0,
  225. .max_length = 0,
  226. .decl_required = true,
  227. .type_required = false,
  228. .function_type_required = false,
  229. .handler = handle_latent_entropy_attribute,
  230. #if BUILDING_GCC_VERSION >= 4007
  231. .affects_type_identity = false
  232. #endif
  233. };
  234. static void register_attributes(void *event_data __unused, void *data __unused)
  235. {
  236. register_attribute(&latent_entropy_attr);
  237. }
  238. static bool latent_entropy_gate(void)
  239. {
  240. tree list;
  241. /* don't bother with noreturn functions for now */
  242. if (TREE_THIS_VOLATILE(current_function_decl))
  243. return false;
  244. /* gcc-4.5 doesn't discover some trivial noreturn functions */
  245. if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0)
  246. return false;
  247. list = DECL_ATTRIBUTES(current_function_decl);
  248. return lookup_attribute("latent_entropy", list) != NULL_TREE;
  249. }
  250. static tree create_var(tree type, const char *name)
  251. {
  252. tree var;
  253. var = create_tmp_var(type, name);
  254. add_referenced_var(var);
  255. mark_sym_for_renaming(var);
  256. return var;
  257. }
  258. /*
  259. * Set up the next operation and its constant operand to use in the latent
  260. * entropy PRNG. When RHS is specified, the request is for perturbing the
  261. * local latent entropy variable, otherwise it is for perturbing the global
  262. * latent entropy variable where the two operands are already given by the
  263. * local and global latent entropy variables themselves.
  264. *
  265. * The operation is one of add/xor/rol when instrumenting the local entropy
  266. * variable and one of add/xor when perturbing the global entropy variable.
  267. * Rotation is not used for the latter case because it would transmit less
  268. * entropy to the global variable than the other two operations.
  269. */
  270. static enum tree_code get_op(tree *rhs)
  271. {
  272. static enum tree_code op;
  273. unsigned HOST_WIDE_INT random_const;
  274. random_const = get_random_const();
  275. switch (op) {
  276. case BIT_XOR_EXPR:
  277. op = PLUS_EXPR;
  278. break;
  279. case PLUS_EXPR:
  280. if (rhs) {
  281. op = LROTATE_EXPR;
  282. /*
  283. * This code limits the value of random_const to
  284. * the size of a long for the rotation
  285. */
  286. random_const %= TYPE_PRECISION(long_unsigned_type_node);
  287. break;
  288. }
  289. case LROTATE_EXPR:
  290. default:
  291. op = BIT_XOR_EXPR;
  292. break;
  293. }
  294. if (rhs)
  295. *rhs = build_int_cstu(long_unsigned_type_node, random_const);
  296. return op;
  297. }
  298. static gimple create_assign(enum tree_code code, tree lhs, tree op1,
  299. tree op2)
  300. {
  301. return gimple_build_assign_with_ops(code, lhs, op1, op2);
  302. }
  303. static void perturb_local_entropy(basic_block bb, tree local_entropy)
  304. {
  305. gimple_stmt_iterator gsi;
  306. gimple assign;
  307. tree rhs;
  308. enum tree_code op;
  309. op = get_op(&rhs);
  310. assign = create_assign(op, local_entropy, local_entropy, rhs);
  311. gsi = gsi_after_labels(bb);
  312. gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
  313. update_stmt(assign);
  314. }
  315. static void __perturb_latent_entropy(gimple_stmt_iterator *gsi,
  316. tree local_entropy)
  317. {
  318. gimple assign;
  319. tree temp;
  320. enum tree_code op;
  321. /* 1. create temporary copy of latent_entropy */
  322. temp = create_var(long_unsigned_type_node, "temp_latent_entropy");
  323. /* 2. read... */
  324. add_referenced_var(latent_entropy_decl);
  325. mark_sym_for_renaming(latent_entropy_decl);
  326. assign = gimple_build_assign(temp, latent_entropy_decl);
  327. gsi_insert_before(gsi, assign, GSI_NEW_STMT);
  328. update_stmt(assign);
  329. /* 3. ...modify... */
  330. op = get_op(NULL);
  331. assign = create_assign(op, temp, temp, local_entropy);
  332. gsi_insert_after(gsi, assign, GSI_NEW_STMT);
  333. update_stmt(assign);
  334. /* 4. ...write latent_entropy */
  335. assign = gimple_build_assign(latent_entropy_decl, temp);
  336. gsi_insert_after(gsi, assign, GSI_NEW_STMT);
  337. update_stmt(assign);
  338. }
  339. static bool handle_tail_calls(basic_block bb, tree local_entropy)
  340. {
  341. gimple_stmt_iterator gsi;
  342. for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
  343. gcall *call;
  344. gimple stmt = gsi_stmt(gsi);
  345. if (!is_gimple_call(stmt))
  346. continue;
  347. call = as_a_gcall(stmt);
  348. if (!gimple_call_tail_p(call))
  349. continue;
  350. __perturb_latent_entropy(&gsi, local_entropy);
  351. return true;
  352. }
  353. return false;
  354. }
  355. static void perturb_latent_entropy(tree local_entropy)
  356. {
  357. edge_iterator ei;
  358. edge e, last_bb_e;
  359. basic_block last_bb;
  360. gcc_assert(single_pred_p(EXIT_BLOCK_PTR_FOR_FN(cfun)));
  361. last_bb_e = single_pred_edge(EXIT_BLOCK_PTR_FOR_FN(cfun));
  362. FOR_EACH_EDGE(e, ei, last_bb_e->src->preds) {
  363. if (ENTRY_BLOCK_PTR_FOR_FN(cfun) == e->src)
  364. continue;
  365. if (EXIT_BLOCK_PTR_FOR_FN(cfun) == e->src)
  366. continue;
  367. handle_tail_calls(e->src, local_entropy);
  368. }
  369. last_bb = single_pred(EXIT_BLOCK_PTR_FOR_FN(cfun));
  370. if (!handle_tail_calls(last_bb, local_entropy)) {
  371. gimple_stmt_iterator gsi = gsi_last_bb(last_bb);
  372. __perturb_latent_entropy(&gsi, local_entropy);
  373. }
  374. }
  375. static void init_local_entropy(basic_block bb, tree local_entropy)
  376. {
  377. gimple assign, call;
  378. tree frame_addr, rand_const, tmp, fndecl, udi_frame_addr;
  379. enum tree_code op;
  380. unsigned HOST_WIDE_INT rand_cst;
  381. gimple_stmt_iterator gsi = gsi_after_labels(bb);
  382. /* 1. create local_entropy_frameaddr */
  383. frame_addr = create_var(ptr_type_node, "local_entropy_frameaddr");
  384. /* 2. local_entropy_frameaddr = __builtin_frame_address() */
  385. fndecl = builtin_decl_implicit(BUILT_IN_FRAME_ADDRESS);
  386. call = gimple_build_call(fndecl, 1, integer_zero_node);
  387. gimple_call_set_lhs(call, frame_addr);
  388. gsi_insert_before(&gsi, call, GSI_NEW_STMT);
  389. update_stmt(call);
  390. udi_frame_addr = fold_convert(long_unsigned_type_node, frame_addr);
  391. assign = gimple_build_assign(local_entropy, udi_frame_addr);
  392. gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
  393. update_stmt(assign);
  394. /* 3. create temporary copy of latent_entropy */
  395. tmp = create_var(long_unsigned_type_node, "temp_latent_entropy");
  396. /* 4. read the global entropy variable into local entropy */
  397. add_referenced_var(latent_entropy_decl);
  398. mark_sym_for_renaming(latent_entropy_decl);
  399. assign = gimple_build_assign(tmp, latent_entropy_decl);
  400. gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
  401. update_stmt(assign);
  402. /* 5. mix local_entropy_frameaddr into local entropy */
  403. assign = create_assign(BIT_XOR_EXPR, local_entropy, local_entropy, tmp);
  404. gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
  405. update_stmt(assign);
  406. rand_cst = get_random_const();
  407. rand_const = build_int_cstu(long_unsigned_type_node, rand_cst);
  408. op = get_op(NULL);
  409. assign = create_assign(op, local_entropy, local_entropy, rand_const);
  410. gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
  411. update_stmt(assign);
  412. }
  413. static bool create_latent_entropy_decl(void)
  414. {
  415. varpool_node_ptr node;
  416. if (latent_entropy_decl != NULL_TREE)
  417. return true;
  418. FOR_EACH_VARIABLE(node) {
  419. tree name, var = NODE_DECL(node);
  420. if (DECL_NAME_LENGTH(var) < sizeof("latent_entropy") - 1)
  421. continue;
  422. name = DECL_NAME(var);
  423. if (strcmp(IDENTIFIER_POINTER(name), "latent_entropy"))
  424. continue;
  425. latent_entropy_decl = var;
  426. break;
  427. }
  428. return latent_entropy_decl != NULL_TREE;
  429. }
  430. static unsigned int latent_entropy_execute(void)
  431. {
  432. basic_block bb;
  433. tree local_entropy;
  434. if (!create_latent_entropy_decl())
  435. return 0;
  436. /* prepare for step 2 below */
  437. gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
  438. bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
  439. if (!single_pred_p(bb)) {
  440. split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
  441. gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
  442. bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
  443. }
  444. /* 1. create the local entropy variable */
  445. local_entropy = create_var(long_unsigned_type_node, "local_entropy");
  446. /* 2. initialize the local entropy variable */
  447. init_local_entropy(bb, local_entropy);
  448. bb = bb->next_bb;
  449. /*
  450. * 3. instrument each BB with an operation on the
  451. * local entropy variable
  452. */
  453. while (bb != EXIT_BLOCK_PTR_FOR_FN(cfun)) {
  454. perturb_local_entropy(bb, local_entropy);
  455. bb = bb->next_bb;
  456. };
  457. /* 4. mix local entropy into the global entropy variable */
  458. perturb_latent_entropy(local_entropy);
  459. return 0;
  460. }
  461. static void latent_entropy_start_unit(void *gcc_data __unused,
  462. void *user_data __unused)
  463. {
  464. tree type, id;
  465. int quals;
  466. seed = get_random_seed(false);
  467. if (in_lto_p)
  468. return;
  469. /* extern volatile unsigned long latent_entropy */
  470. quals = TYPE_QUALS(long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
  471. type = build_qualified_type(long_unsigned_type_node, quals);
  472. id = get_identifier("latent_entropy");
  473. latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
  474. TREE_STATIC(latent_entropy_decl) = 1;
  475. TREE_PUBLIC(latent_entropy_decl) = 1;
  476. TREE_USED(latent_entropy_decl) = 1;
  477. DECL_PRESERVE_P(latent_entropy_decl) = 1;
  478. TREE_THIS_VOLATILE(latent_entropy_decl) = 1;
  479. DECL_EXTERNAL(latent_entropy_decl) = 1;
  480. DECL_ARTIFICIAL(latent_entropy_decl) = 1;
  481. lang_hooks.decls.pushdecl(latent_entropy_decl);
  482. }
  483. #define PASS_NAME latent_entropy
  484. #define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
  485. #define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
  486. | TODO_update_ssa
  487. #include "gcc-generate-gimple-pass.h"
  488. __visible int plugin_init(struct plugin_name_args *plugin_info,
  489. struct plugin_gcc_version *version)
  490. {
  491. bool enabled = true;
  492. const char * const plugin_name = plugin_info->base_name;
  493. const int argc = plugin_info->argc;
  494. const struct plugin_argument * const argv = plugin_info->argv;
  495. int i;
  496. struct register_pass_info latent_entropy_pass_info;
  497. latent_entropy_pass_info.pass = make_latent_entropy_pass();
  498. latent_entropy_pass_info.reference_pass_name = "optimized";
  499. latent_entropy_pass_info.ref_pass_instance_number = 1;
  500. latent_entropy_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
  501. static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
  502. {
  503. .base = &latent_entropy_decl,
  504. .nelt = 1,
  505. .stride = sizeof(latent_entropy_decl),
  506. .cb = &gt_ggc_mx_tree_node,
  507. .pchw = &gt_pch_nx_tree_node
  508. },
  509. LAST_GGC_ROOT_TAB
  510. };
  511. if (!plugin_default_version_check(version, &gcc_version)) {
  512. error(G_("incompatible gcc/plugin versions"));
  513. return 1;
  514. }
  515. for (i = 0; i < argc; ++i) {
  516. if (!(strcmp(argv[i].key, "disable"))) {
  517. enabled = false;
  518. continue;
  519. }
  520. error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
  521. }
  522. register_callback(plugin_name, PLUGIN_INFO, NULL,
  523. &latent_entropy_plugin_info);
  524. if (enabled) {
  525. register_callback(plugin_name, PLUGIN_START_UNIT,
  526. &latent_entropy_start_unit, NULL);
  527. register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
  528. NULL, (void *)&gt_ggc_r_gt_latent_entropy);
  529. register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
  530. &latent_entropy_pass_info);
  531. }
  532. register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
  533. NULL);
  534. return 0;
  535. }