1 /*
2  * Copyright (c) 2022-2024, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <CppUTest/TestHarness.h>
8 #include <vector>
9 
10 #include "protocols/service/fwu/status.h"
11 #include "service/fwu/test/fwu_dut/fwu_dut.h"
12 #include "service/fwu/test/fwu_dut_factory/fwu_dut_factory.h"
13 #include "service/fwu/test/image_directory_checker/image_directory_checker.h"
14 
15 /*
16  * Tests that check defenses against invalid behaviour from a client.
17  */
TEST_GROUP(FwuInvalidBehaviourTests)18 TEST_GROUP(FwuInvalidBehaviourTests)
19 {
20 	void setup()
21 	{
22 		m_dut = NULL;
23 		m_fwu_client = NULL;
24 		m_dir_checker = new image_directory_checker;
25 	}
26 
27 	void teardown()
28 	{
29 		delete m_dir_checker;
30 		m_dir_checker = NULL;
31 
32 		delete m_fwu_client;
33 		m_fwu_client = NULL;
34 
35 		delete m_dut;
36 		m_dut = NULL;
37 	}
38 
39 	fwu_dut *m_dut;
40 	image_directory_checker *m_dir_checker;
41 	fwu_client *m_fwu_client;
42 };
43 
TEST(FwuInvalidBehaviourTests,invalidOperationsInRegular)44 TEST(FwuInvalidBehaviourTests, invalidOperationsInRegular)
45 {
46 	int status = 0;
47 	uint32_t stream_handle = 0;
48 	struct uuid_octets uuid;
49 
50 	/* Construct and boot a DUT with a single fw location. Assume
51 	 * an initial transition to the REGULAR FWU state.
52 	 */
53 	m_dut = fwu_dut_factory::create(1);
54 	m_fwu_client = m_dut->create_fwu_client();
55 	m_dut->boot();
56 
57 	/* End staging without having entered STAGING */
58 	status = m_fwu_client->end_staging();
59 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
60 
61 	/* Cancel staging without having entered STAGING */
62 	status = m_fwu_client->cancel_staging();
63 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
64 
65 	/* Open a fw image when not STAGING */
66 	m_dut->whole_volume_image_type_uuid(0, &uuid);
67 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
68 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
69 
70 	/* Write to a stream when not STAGING. Note also that it's not possible
71 	 * to obtain a stream handle outside of STAGING so use an arbitrary value.
72 	 * There are a few possible failure cases, depending on whether the stream
73 	 * handle qualifies an open stream or not. For this case, it shouldn't.
74 	 */
75 	std::string image_data("some image data...");
76 
77 	stream_handle = 67;
78 	status = m_fwu_client->write_stream(stream_handle,
79 					    reinterpret_cast<const uint8_t *>(image_data.data()),
80 					    image_data.size());
81 	LONGS_EQUAL(FWU_STATUS_UNKNOWN, status);
82 
83 	/* An attempt to commit with an invalid handle should fail in a similar way */
84 	stream_handle = 771;
85 	status = m_fwu_client->commit(stream_handle, false);
86 	LONGS_EQUAL(FWU_STATUS_UNKNOWN, status);
87 }
88 
TEST(FwuInvalidBehaviourTests,invalidOperationsInStaging)89 TEST(FwuInvalidBehaviourTests, invalidOperationsInStaging)
90 {
91 	int status = 0;
92 	struct uuid_octets uuid;
93 
94 	/* Construct and boot a DUT with a 3 fw locations and with
95 	 * the policy not to allow partial updates. This means that
96 	 * an incoming update must include image updates for all
97 	 * locations.
98 	 *
99 	 * Assume an initial transition to the REGULAR FWU state.
100 	 */
101 	m_dut = fwu_dut_factory::create(3, false);
102 	m_fwu_client = m_dut->create_fwu_client();
103 	m_dut->boot();
104 
105 	/* Expect to be able to transition to STAGING */
106 	status = m_fwu_client->begin_staging(0, 0, NULL);
107 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
108 
109 	/* And re-enter STAGING (implicit cancel) */
110 	status = m_fwu_client->begin_staging(0, 0, NULL);
111 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
112 
113 	/* Opening a couple of streams for installing images associated
114 	 * with two of the three updatable locations.
115 	 */
116 	uint32_t stream_handle1 = 0;
117 	uint32_t stream_handle2 = 0;
118 
119 	m_dut->whole_volume_image_type_uuid(0, &uuid);
120 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle1);
121 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
122 
123 	m_dut->whole_volume_image_type_uuid(1, &uuid);
124 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle2);
125 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
126 
127 	/* Attempting to end staging with open install streams should fail */
128 	status = m_fwu_client->end_staging();
129 	LONGS_EQUAL(FWU_STATUS_BUSY, status);
130 
131 	/* Commit without actually writing any image data. This would occur
132 	 * if an incoming update package contained zero length images. This
133 	 * could be interpreted as an image delete. As a delete operation
134 	 * is not supported by the raw_installer used by the DUT, expect
135 	 * the commit operations to fail.
136 	 */
137 	status = m_fwu_client->commit(stream_handle1, false);
138 	LONGS_EQUAL(FWU_STATUS_NOT_AVAILABLE, status);
139 	status = m_fwu_client->commit(stream_handle2, false);
140 	LONGS_EQUAL(FWU_STATUS_NOT_AVAILABLE, status);
141 
142 	/* Expect accepting images to be denied while STAGING */
143 	m_dut->whole_volume_image_type_uuid(0, &uuid);
144 	status = m_fwu_client->accept(&uuid);
145 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
146 
147 	m_dut->whole_volume_image_type_uuid(1, &uuid);
148 	status = m_fwu_client->accept(&uuid);
149 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
150 
151 	/* Expect rolling back to previous version to also be denied while STAGING */
152 	status = m_fwu_client->select_previous();
153 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
154 
155 	/* Attempting to end staging when errors occurred during the installation
156 	 * process should fail.
157 	 */
158 	status = m_fwu_client->end_staging();
159 	LONGS_EQUAL(FWU_STATUS_NOT_AVAILABLE, status);
160 
161 	/* Because the end_staging failed, expect to be still in STAGING.
162 	 * A call to cancel_staging is required to force a transition back to
163 	 * REGULAR.
164 	 */
165 	status = m_fwu_client->cancel_staging();
166 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
167 
168 	/* Cancelling again though should fail. */
169 	status = m_fwu_client->cancel_staging();
170 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
171 }
172 
TEST(FwuInvalidBehaviourTests,invalidOperationsInTrial)173 TEST(FwuInvalidBehaviourTests, invalidOperationsInTrial)
174 {
175 	int status = 0;
176 	struct uuid_octets uuid;
177 
178 	/* Construct and boot a DUT with a 3 fw locations and with
179 	 * the policy to allow partial updates. With this policy
180 	 * not all locations need to be updated for a viable update.
181 	 */
182 	m_dut = fwu_dut_factory::create(3, true);
183 	m_fwu_client = m_dut->create_fwu_client();
184 	m_dut->boot();
185 
186 	/* Expect to be able to transition to STAGING */
187 	status = m_fwu_client->begin_staging(0, 0, NULL);
188 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
189 
190 	/* Install an image into location 0 */
191 	uint32_t stream_handle = 0;
192 
193 	m_dut->whole_volume_image_type_uuid(0, &uuid);
194 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
195 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
196 
197 	std::vector<uint8_t> image_data;
198 	m_dut->generate_image_data(&image_data);
199 
200 	status = m_fwu_client->write_stream(stream_handle,
201 					    reinterpret_cast<const uint8_t *>(image_data.data()),
202 					    image_data.size());
203 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
204 
205 	status = m_fwu_client->commit(stream_handle, false);
206 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
207 
208 	/* Although only one location was updated, the DUT accepts partial
209 	 * updates so ending staging should be happy.
210 	 */
211 	status = m_fwu_client->end_staging();
212 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
213 
214 	/* If we've transitioned to TRAIL, attempting to begin staging
215 	 * again should be denied.
216 	 */
217 	status = m_fwu_client->begin_staging(0, 0, NULL);
218 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
219 
220 	/* Activate the update. We'd expect the update to have been installed
221 	 * in a different bank from the boot bank.
222 	 */
223 	m_dut->shutdown();
224 	m_dut->boot();
225 
226 	/* If all's well, the DUT should have rebooted to TRIAL. Confirm this by
227 	 * trying to begin staging - this should be denied.
228 	 */
229 	status = m_fwu_client->begin_staging(0, 0, NULL);
230 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
231 
232 	/* All other staging related operations should also be denied */
233 	status = m_fwu_client->end_staging();
234 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
235 
236 	status = m_fwu_client->cancel_staging();
237 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
238 
239 	/* Attempting to install images should also be denied */
240 	m_dut->whole_volume_image_type_uuid(0, &uuid);
241 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
242 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
243 
244 	/* Reading the image directory should be ok though */
245 	status = m_dir_checker->fetch_image_directory(m_fwu_client);
246 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
247 
248 	/* Reverting to the previous version should be ok */
249 	status = m_fwu_client->select_previous();
250 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
251 
252 	/* Should have transitioned back to REGULAR */
253 	status = m_fwu_client->begin_staging(0, 0, NULL);
254 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
255 
256 	status = m_fwu_client->begin_staging(0, 0, NULL);
257 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
258 
259 	status = m_fwu_client->cancel_staging();
260 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
261 
262 	m_dut->shutdown();
263 }
264