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