blob: 4a153f64f9e89f554cbe8e0aad3fc8e72b56bad5 [file] [log] [blame]
Gary Morrisonced8c6f2020-02-27 19:35:59 +00001/*
2 * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include <ctime> // to seed random, if seed not passed in
9#include <string>
10#include <vector>
11#include <iostream>
12#include <cstdlib> // for srand() and rand()
13#include <cstdio> // for template lex&yacc input file
14#include "class_forwards.hpp"
15#include "boilerplate.hpp"
16#include "gibberish.hpp"
17#include "compute.hpp"
18#include "string_ops.hpp"
19#include "data_blocks.hpp"
20#include "psa_asset.hpp"
21#include "find_or_create_asset.hpp"
22#include "template_line.hpp"
23#include "tf_fuzz.hpp"
24#include "sst_asset.hpp"
25#include "crypto_asset.hpp"
26#include "psa_call.hpp"
27#include "tf_fuzz_grammar.tab.hpp"
28
29
30extern FILE* yyin; // telling lex&yacc which file to parse
31
32using namespace std;
33
34long psa_asset::unique_id_counter = 10; // counts unique IDs for assets
35long psa_call::unique_id_counter = 10; // counts unique IDs for assets
36 /* FYI: Must initialize these class variables outside the class. If
37 initialized inside the class, g++ requires they be const. */
38
39/**********************************************************************************
40 Methods of class tf_fuzz_info follow:
41**********************************************************************************/
42
43asset_search tf_fuzz_info::find_or_create_sst_asset (
44 psa_asset_search criterion, // what to search on
45 psa_asset_usage where, // where to search
46 string target_name, // ignored if not searching on name
47 uint64_t target_id, // also ignored if not searching on ID (e.g., SST UID)
48 long &serial_no, // search by asset's unique serial number
49 bool create_asset, // true to create the asset if it doesn't exist
50 vector<psa_asset*>::iterator &asset // returns a pointer to requested asset
51) {
52 return generic_find_or_create_asset<sst_asset>(
53 active_sst_asset, deleted_sst_asset,
54 invalid_sst_asset, criterion, where, target_name, target_id,
55 serial_no, create_asset, asset
56 );
57}
58
59asset_search tf_fuzz_info::find_or_create_key_asset (
60 psa_asset_search criterion, // what to search on
61 psa_asset_usage where, // where to search
62 string target_name, // ignored if not searching on name
63 uint64_t target_id, // also ignored if not searching on ID (e.g., SST UID)
64 long &serial_no, // search by asset's unique serial number
65 bool create_asset, // true to create the asset if it doesn't exist
66 vector<psa_asset*>:: iterator &asset // returns iterator to requested asset
67) {
68 return generic_find_or_create_asset<key_asset>(
69 active_key_asset, deleted_key_asset,
70 invalid_key_asset, criterion, where, target_name, target_id,
71 serial_no, create_asset, asset
72 );
73}
74
75asset_search tf_fuzz_info::find_or_create_policy_asset (
76 psa_asset_search criterion, // what to search on
77 psa_asset_usage where, // where to search
78 string target_name, // ignored unless searching on name
79 uint64_t target_id, // also ignored unless searching on ID (e.g., SST UID)
80 long &serial_no, // search by asset's unique serial number
81 bool create_asset, // true to create the asset if it doesn't exist
82 vector<psa_asset*>::iterator &asset // returns iterator to requested asset
83) {
84 return generic_find_or_create_asset<policy_asset>(
85 active_policy_asset, deleted_policy_asset,
86 invalid_policy_asset, criterion, where, target_name, target_id,
87 serial_no, create_asset, asset
88 );
89}
90
91asset_search tf_fuzz_info::find_or_create_psa_asset (
92 psa_asset_type asset_type, // what type of asset to find
93 psa_asset_search criterion, // what to search on
94 psa_asset_usage where, // where to search
95 string target_name, // ignored if not searching on name
96 uint64_t target_id, // also ignored if not searching on ID (e.g., SST UID)
97 long &serial_no, // search by asset's unique serial number
98 bool create_asset, // true to create the asset if it doesn't exist
99 vector<psa_asset*>::iterator &asset // returns iterator to asset
100) {
101 switch (asset_type) {
102 case psa_asset_type::sst:
103 return find_or_create_sst_asset (
104 criterion, where, target_name, target_id,
105 serial_no, create_asset, asset);
106 break;
107 case psa_asset_type::key:
108 return find_or_create_key_asset (
109 criterion, where, target_name, target_id,
110 serial_no, create_asset, asset);
111 break;
112 case psa_asset_type::policy:
113 return find_or_create_policy_asset (
114 criterion, where, target_name, target_id,
115 serial_no, create_asset, asset);
116 break;
117 default:
118 cerr << "\nError: Internal: Please report error "
119 << "#1503 to TF-Fuzz developers." << endl;
120 exit (1500);
121 }
122}
123
124// Remove any PSA resources used in the test. Returns success==true, fail==false.
125void tf_fuzz_info::teardown_test (void)
126{
127 string call;
128 // Traverse through the SST-assets list, writing out remove commands:
129 for (auto &asset : active_sst_asset) {
130 call = bplate->bplate_string[teardown_sst];
131 find_replace_1st ("$uid", to_string(asset->asset_id.id_n), call);
132 call.append (bplate->bplate_string[teardown_sst_check]);
133 output_C_file << call;
134 }
135 // Same, but with key assets:
136 for (auto &asset : active_key_asset) {
137 call = bplate->bplate_string[teardown_key];
138 find_replace_1st ("$handle", asset->handle_str, call);
139 call.append (bplate->bplate_string[teardown_key_check]);
140 output_C_file << call;
141 }
142}
143
144// Write out the test itself.
145void tf_fuzz_info::write_test (void)
146{
147 string call;
148 string work = bplate->bplate_string[preamble_A]; // a temporary workspace string
149
150 // The test file should be open before calling this method.
151 // Spit out the obligatory preamble:
152 find_replace_all ("$purpose", test_purpose, work);
153 output_C_file << work;
154
155 // If using hashing, then spit out the hashing functions:
156 if (include_hashing_code) {
157 work = bplate->bplate_string[hashing_code];
158 output_C_file << work;
159 }
160
161 // Print out the second half of the preamble code:
162 work = bplate->bplate_string[preamble_B];
163 find_replace_all ("$purpose", test_purpose, work);
164 output_C_file << work;
165
166 output_C_file << "\n\n /* Variables (etc.) to initialize and check PSA "
167 << "assets: */" << endl;
168 for (auto call : calls) {
169 // Reminder: calls is a vector of *pointers to* psa_call subclass objects.
170 call->fill_in_prep_code();
171 call->write_out_prep_code (output_C_file);
172 }
173
174 output_C_file << "\n\n /* PSA calls to test: */" << endl;
175 for (auto call : calls) {
176 call->fill_in_command(); // (fills in check code too)
177 output_C_file << endl;
178 call->write_out_command (output_C_file);
179 call->write_out_check_code (output_C_file);
180 }
181
182 output_C_file << "\n\n /* Removing assets left over from testing: */"
183 << endl;
184 teardown_test();
185
186 // Seal the deal:
187 output_C_file << bplate->bplate_string[closeout];
188
189 // Close the template and test files:
190 output_C_file.close();
191 fclose (template_file);
192}
193
194
195/* Parse command-line parameters. exit() if error(s) found. Place results
196 into resource object. */
197void tf_fuzz_info::parse_cmd_line_params (int argc, char* argv[])
198{
199 int exit_val = 0; // the linux return value, default 0, meaning all-good
200 vector<string> cmd_line_parameter, cmd_line_switch;
201 // (STL) vectors of hard cmd_line_parameter and cmd_line_switches
202 int n_parameters = 0, n_switches = 0;
203 // counting off cmd_line_parameter and cmd_line_switches while parsing
204 char testc;
205
206 // Parse arguments into lists of strings:
207 for (int i = 1; i < argc; ++i) {
208 if (argv[i][0] == '-') { // cmd_line_switch
209 if (argv[i][1] == '-') { // double-dash
210 cmd_line_switch.push_back (string(argv[i]+2));
211 } else { // single-dash cmd_line_switch; fine either way
212 cmd_line_switch.push_back (string(argv[i]+1));
213 }
214 ++n_switches;
215 } else { // hard cmd_line_parameter
216 cmd_line_parameter.push_back(argv[i]);
217 ++n_parameters;
218 }
219 }
220 // If too-few or too many cmd_line_parameter supplied
221 for (int i = 0; i < n_switches; ++i) {
222 // If usage string requested...
223 if (cmd_line_switch[i] == "h") {
224 exit_val = 10;
225 }
226 // If verbose requested, make note:
227 if (cmd_line_switch[i] == "v") {
228 verbose_mode = true;
229 }
230 }
231 if (exit_val == 10) { // -h switch
232 cout << "\nHow to run TF-Fuzz:" << endl;
233 } else if (n_parameters < 2) {
234 cerr << "\nToo few command-line parameters." << endl;
235 exit_val = 11;
236 } else if (n_parameters > 3) {
237 cerr << "\nToo many command-line parameters." << endl;
238 exit_val = 12;
239 } else {
240 template_file_name = cmd_line_parameter[0];
241 template_file = fopen (template_file_name.c_str(), "r");
242 test_output_file_name = cmd_line_parameter[1];
243 output_C_file.open (test_output_file_name, ios::out);
244 if (n_parameters == 3) {
245 /* TODO: The try-catch below doesn't always seem to work. For now,
246 manually "catch" the most basic problem: */
247 testc = cmd_line_parameter[2][0];
248 if (testc < '0' || testc > '9') {
249 cerr << "\nError: Random-seed value (third parameter) could "
250 << "not be interpreted as a number." << endl;
251 rand_seed = 0;
252 } else {
253 try {
254 rand_seed = stol (cmd_line_parameter[2], 0, 0);
255 } catch (int excep) {
256 excep = 0; // (keep compiler from complaining about not using excep)
257 cerr << "\nWarning: Random-seed value (third parameter) could "
258 << "not be interpreted as a number." << endl;
259 rand_seed = 0;
260 }
261 }
262 }
263 if (rand_seed == 0 || n_parameters < 3) {
264 if (n_parameters < 3) {
265 cout << "Info: random seed was not specified." << endl;
266 } else {
267 cout << "Warning: random seed, " << cmd_line_parameter[2]
268 << ", was not usable!" << endl;
269 }
270 srand((unsigned int) time(0)); // TODO: ideally, XOR or add in PID#
271 rand_seed = rand();
272 /* doesn't really matter, but it just "feels better" when the
273 default seed value is itself more random. */
274 }
275 cout << endl << "Using seed value of " << dec << rand_seed << " " << hex
276 << "(0x" << rand_seed << ")." << endl;
277 srand(rand_seed);
278 if (template_file == NULL) {
279 cerr << "\nError: Template file " << template_file_name
280 << " could not be opened." << endl;
281 exit_val = 13;
282 } else if (!output_C_file.is_open()) {
283 // If test-output file couldn't be opened
284 cerr << "\nError: Output C test file " << test_output_file_name
285 << " could not be opened." << endl;
286 exit_val = 14;
287 }
288 // Default (not entirely worthless) purpose of the test:
289 test_purpose.assign ( "template = " + template_file_name + ", seed = "
290 + to_string(rand_seed));
291 }
292 // Bad command line, or request for usage blurb, so tell them how to run us:
293 if (exit_val != 0) {
294 cout << endl << argv[0] << " usage:" << endl;
295 cout << " Basic cmd_line_parameter (positional, in order, "
296 << "left-to-right):" << endl;
297 cout << " Test-template file" << endl;
298 cout << " Test-output .c file" << endl;
299 cout << " (optional) random seed value" << endl;
300 cout << " Optional switches:" << endl;
301 cout << " -h or --h: This help (command-line usage) summary."
302 << endl;
303 cout << " -v or --v: Verbose mode." << endl;
304 cout << "Examples:" << endl;
305 cout << " " << argv[0] << " -h" << endl;
306 cout << " " << argv[0] << " template.txt output_test.c 0x5EED" << endl;
307 exit (exit_val);
308 }
309}
310
311tf_fuzz_info::tf_fuzz_info (void) // (constructor)
312{
313 this->bplate = new boilerplate();
314 test_purpose = template_file_name = test_output_file_name = "";
315 rand_seed = 0;
316 verbose_mode = false;
317 include_hashing_code = false; // default
318}
319
320tf_fuzz_info::~tf_fuzz_info (void)
321{
322 delete bplate;
323}
324
325/**********************************************************************************
326 End of methods of class tf_fuzz_info.
327**********************************************************************************/
328
329
330int main(int argc, char* argv[])
331{
332 cout << "Trusted Firmware Fuzzer (TF-Fuzz) starting..." << endl << endl;
333
334 // Allocate "the world":
335 tf_fuzz_info *rsrc = new tf_fuzz_info;
336
337 // Parse parameters and open files:
338 rsrc->parse_cmd_line_params (argc, argv);
339
340 // Parse the test-template file:
341 yyin = rsrc->template_file;
342 int parse_result = yyparse (rsrc);
343
344 if (parse_result == 1) {
345 cerr << "\nError: Template file has errors." << endl;
346 } else if (parse_result == 2) {
347 cerr << "\nError: Sorry, TF-Fuzz ran out of memory." << endl;
348 }
349
350 cout << "Writing test file, " << rsrc->test_output_file_name << "." << endl;
351 rsrc->write_test();
352 rsrc->output_C_file.close();
353
354 cout << endl << "TF-Fuzz test generation complete." << endl;
355 return 0;
356}