blob: e4573a519695acd2db2874c25add12f786e1929a [file] [log] [blame]
Patrick Williams92b42cb2022-09-03 06:53:57 -05001Upstream-Status: Pending
2Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com>
3
4From 2d975e5ec5df6f81d6c35fe927f72d49181142f8 Mon Sep 17 00:00:00 2001
5From: Julian Hall <julian.hall@arm.com>
6Date: Tue, 19 Jul 2022 12:43:30 +0100
7Subject: [PATCH] Fix UEFI get_variable with small buffer
8
9The handling of the UEFI get_variable operation was incorrect when
10a small or zero data length was specified by a requester. A zero
11length data length is a legitimate way to discover the size of a
12variable without actually retrieving its data. This change adds
13test cases that reproduce the problem and a fix.
14
15Signed-off-by: Julian Hall <julian.hall@arm.com>
16Change-Id: Iec087fbf9305746d1438888e871602ec0ce15824
17---
18 .../backend/test/variable_store_tests.cpp | 60 ++++++++++++++++--
19 .../backend/uefi_variable_store.c | 46 +++++++++++---
20 .../client/cpp/smm_variable_client.cpp | 33 +++++-----
21 .../client/cpp/smm_variable_client.h | 8 ++-
22 .../provider/smm_variable_provider.c | 2 +-
23 .../service/smm_variable_service_tests.cpp | 62 +++++++++++++++++++
24 6 files changed, 179 insertions(+), 32 deletions(-)
25
26diff --git a/components/service/smm_variable/backend/test/variable_store_tests.cpp b/components/service/smm_variable/backend/test/variable_store_tests.cpp
27index 235642e6..98faf761 100644
28--- a/components/service/smm_variable/backend/test/variable_store_tests.cpp
29+++ b/components/service/smm_variable/backend/test/variable_store_tests.cpp
30@@ -128,7 +128,8 @@ TEST_GROUP(UefiVariableStoreTests)
31
32 efi_status_t get_variable(
33 const std::wstring &name,
34- std::string &data)
35+ std::string &data,
36+ size_t data_len_clamp = VARIABLE_BUFFER_SIZE)
37 {
38 std::vector<int16_t> var_name = to_variable_name(name);
39 size_t name_size = var_name.size() * sizeof(int16_t);
40@@ -144,21 +145,40 @@ TEST_GROUP(UefiVariableStoreTests)
41 access_variable->NameSize = name_size;
42 memcpy(access_variable->Name, var_name.data(), name_size);
43
44- access_variable->DataSize = 0;
45+ size_t max_data_len = (data_len_clamp == VARIABLE_BUFFER_SIZE) ?
46+ VARIABLE_BUFFER_SIZE -
47+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(access_variable) :
48+ data_len_clamp;
49+
50+ access_variable->DataSize = max_data_len;
51
52 efi_status_t status = uefi_variable_store_get_variable(
53 &m_uefi_variable_store,
54 access_variable,
55- VARIABLE_BUFFER_SIZE -
56- SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(access_variable),
57+ max_data_len,
58 &total_size);
59
60+ data.clear();
61+
62 if (status == EFI_SUCCESS) {
63
64 const char *data_start = (const char*)(msg_buffer +
65 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(access_variable));
66
67 data = std::string(data_start, access_variable->DataSize);
68+
69+ UNSIGNED_LONGLONGS_EQUAL(
70+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(access_variable),
71+ total_size);
72+ }
73+ else if (status == EFI_BUFFER_TOO_SMALL) {
74+
75+ /* String length set to reported variable length */
76+ data.insert(0, access_variable->DataSize, '!');
77+
78+ UNSIGNED_LONGLONGS_EQUAL(
79+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(access_variable),
80+ total_size);
81 }
82
83 return status;
84@@ -336,6 +356,38 @@ TEST(UefiVariableStoreTests, persistentSetGet)
85 LONGS_EQUAL(0, input_data.compare(output_data));
86 }
87
88+TEST(UefiVariableStoreTests, getWithSmallBuffer)
89+{
90+ efi_status_t status = EFI_SUCCESS;
91+ std::wstring var_name = L"test_variable";
92+ std::string input_data = "quick brown fox";
93+ std::string output_data;
94+
95+ /* A get with a zero length buffer is a legitimate way to
96+ * discover the variable size. This test performs GetVariable
97+ * operations with various buffer small buffer sizes. */
98+ status = set_variable(var_name, input_data, 0);
99+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
100+
101+ /* First get the variable without a constrained buffer */
102+ status = get_variable(var_name, output_data);
103+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
104+
105+ /* Expect got variable data to be the same as the set value */
106+ UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
107+ LONGS_EQUAL(0, input_data.compare(output_data));
108+
109+ /* Now try with a zero length buffer */
110+ status = get_variable(var_name, output_data, 0);
111+ UNSIGNED_LONGLONGS_EQUAL(EFI_BUFFER_TOO_SMALL, status);
112+ UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
113+
114+ /* Try with a non-zero length but too small buffer */
115+ status = get_variable(var_name, output_data, input_data.size() -1);
116+ UNSIGNED_LONGLONGS_EQUAL(EFI_BUFFER_TOO_SMALL, status);
117+ UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
118+}
119+
120 TEST(UefiVariableStoreTests, removeVolatile)
121 {
122 efi_status_t status = EFI_SUCCESS;
123diff --git a/components/service/smm_variable/backend/uefi_variable_store.c b/components/service/smm_variable/backend/uefi_variable_store.c
124index e8771c21..90d648de 100644
125--- a/components/service/smm_variable/backend/uefi_variable_store.c
126+++ b/components/service/smm_variable/backend/uefi_variable_store.c
127@@ -1,5 +1,5 @@
128 /*
129- * Copyright (c) 2021, Arm Limited. All rights reserved.
130+ * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
131 *
132 * SPDX-License-Identifier: BSD-3-Clause
133 *
134@@ -294,7 +294,10 @@ efi_status_t uefi_variable_store_get_variable(
135
136 status = load_variable_data(context, info, var, max_data_len);
137 var->Attributes = info->metadata.attributes;
138- *total_length = SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(var);
139+
140+ *total_length = (status == EFI_SUCCESS) ?
141+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(var) :
142+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
143 }
144 }
145
146@@ -682,7 +685,6 @@ static efi_status_t load_variable_data(
147 {
148 EMSG("In func %s\n", __func__);
149 psa_status_t psa_status = PSA_SUCCESS;
150- size_t data_len = 0;
151 uint8_t *data = (uint8_t*)var +
152 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
153
154@@ -692,17 +694,41 @@ static efi_status_t load_variable_data(
155
156 if (delegate_store->storage_backend) {
157
158- psa_status = delegate_store->storage_backend->interface->get(
159+ struct psa_storage_info_t storage_info;
160+
161+ psa_status = delegate_store->storage_backend->interface->get_info(
162 delegate_store->storage_backend->context,
163 context->owner_id,
164 info->metadata.uid,
165- 0,
166- max_data_len,
167- data,
168- &data_len);
169- EMSG("In func %s get status is %d\n", __func__, psa_status);
170+ &storage_info);
171+
172+ if (psa_status == PSA_SUCCESS) {
173
174- var->DataSize = data_len;
175+ size_t get_limit = (var->DataSize < max_data_len) ?
176+ var->DataSize :
177+ max_data_len;
178+
179+ if (get_limit >= storage_info.size) {
180+
181+ size_t got_len = 0;
182+
183+ psa_status = delegate_store->storage_backend->interface->get(
184+ delegate_store->storage_backend->context,
185+ context->owner_id,
186+ info->metadata.uid,
187+ 0,
188+ max_data_len,
189+ data,
190+ &got_len);
191+
192+ var->DataSize = got_len;
193+ }
194+ else {
195+
196+ var->DataSize = storage_info.size;
197+ psa_status = PSA_ERROR_BUFFER_TOO_SMALL;
198+ }
199+ }
200 }
201
202 return psa_to_efi_storage_status(psa_status);
203diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.cpp b/components/service/smm_variable/client/cpp/smm_variable_client.cpp
204index 8438285b..b6b4ed90 100644
205--- a/components/service/smm_variable/client/cpp/smm_variable_client.cpp
206+++ b/components/service/smm_variable/client/cpp/smm_variable_client.cpp
207@@ -1,5 +1,5 @@
208 /*
209- * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
210+ * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
211 *
212 * SPDX-License-Identifier: BSD-3-Clause
213 */
214@@ -122,21 +122,22 @@ efi_status_t smm_variable_client::get_variable(
215 guid,
216 name,
217 data,
218- 0);
219+ 0,
220+ MAX_VAR_DATA_SIZE);
221 }
222
223 efi_status_t smm_variable_client::get_variable(
224 const EFI_GUID &guid,
225 const std::wstring &name,
226 std::string &data,
227- size_t override_name_size)
228+ size_t override_name_size,
229+ size_t max_data_size)
230 {
231 efi_status_t efi_status = EFI_NOT_READY;
232
233 std::vector<int16_t> var_name = to_variable_name(name);
234 size_t name_size = var_name.size() * sizeof(int16_t);
235- size_t data_size = 0;
236- size_t req_len = SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_SIZE(name_size, data_size);
237+ size_t req_len = SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_SIZE(name_size, 0);
238
239 rpc_call_handle call_handle;
240 uint8_t *req_buf;
241@@ -154,7 +155,7 @@ efi_status_t smm_variable_client::get_variable(
242
243 access_var->Guid = guid;
244 access_var->NameSize = name_size;
245- access_var->DataSize = data_size;
246+ access_var->DataSize = max_data_size;
247
248 memcpy(access_var->Name, var_name.data(), name_size);
249
250@@ -168,26 +169,28 @@ efi_status_t smm_variable_client::get_variable(
251
252 efi_status = opstatus;
253
254- if (efi_status == EFI_SUCCESS) {
255-
256- efi_status = EFI_PROTOCOL_ERROR;
257+ if (resp_len >= SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_NAME_OFFSET) {
258
259- if (resp_len >= SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_NAME_OFFSET) {
260+ access_var = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE*)resp_buf;
261+ size_t data_size = access_var->DataSize;
262
263- access_var = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE*)resp_buf;
264+ if (resp_len >=
265+ SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(access_var)) {
266
267- if (resp_len >=
268- SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(access_var)) {
269+ if (efi_status == EFI_SUCCESS) {
270
271- data_size = access_var->DataSize;
272 const char *data_start = (const char*)
273 &resp_buf[
274 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(access_var)];
275
276 data.assign(data_start, data_size);
277- efi_status = EFI_SUCCESS;
278 }
279 }
280+ else if (efi_status == EFI_BUFFER_TOO_SMALL) {
281+
282+ data.clear();
283+ data.insert(0, data_size, '!');
284+ }
285 }
286 }
287 else {
288diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.h b/components/service/smm_variable/client/cpp/smm_variable_client.h
289index c7973916..3d2371a8 100644
290--- a/components/service/smm_variable/client/cpp/smm_variable_client.h
291+++ b/components/service/smm_variable/client/cpp/smm_variable_client.h
292@@ -1,5 +1,5 @@
293 /*
294- * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
295+ * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
296 *
297 * SPDX-License-Identifier: BSD-3-Clause
298 */
299@@ -56,7 +56,8 @@ public:
300 const EFI_GUID &guid,
301 const std::wstring &name,
302 std::string &data,
303- size_t override_name_size);
304+ size_t override_name_size,
305+ size_t max_data_size = MAX_VAR_DATA_SIZE);
306
307 /* Remove a variable */
308 efi_status_t remove_variable(
309@@ -113,6 +114,9 @@ public:
310
311
312 private:
313+
314+ static const size_t MAX_VAR_DATA_SIZE = 65536;
315+
316 efi_status_t rpc_to_efi_status() const;
317
318 static std::vector<int16_t> to_variable_name(const std::wstring &string);
319diff --git a/components/service/smm_variable/provider/smm_variable_provider.c b/components/service/smm_variable/provider/smm_variable_provider.c
320index 1f362c17..95c4fdc9 100644
321--- a/components/service/smm_variable/provider/smm_variable_provider.c
322+++ b/components/service/smm_variable/provider/smm_variable_provider.c
323@@ -165,7 +165,7 @@ static rpc_status_t get_variable_handler(void *context, struct call_req *req)
324 }
325 else {
326
327- /* Reponse buffer not big enough */
328+ /* Response buffer not big enough */
329 efi_status = EFI_BAD_BUFFER_SIZE;
330 }
331 }
332diff --git a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
333index 38c08ebe..989a3e63 100644
334--- a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
335+++ b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
336@@ -284,6 +284,68 @@ TEST(SmmVariableServiceTests, setAndGetNv)
337 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
338 }
339
340+TEST(SmmVariableServiceTests, getVarSize)
341+{
342+ efi_status_t efi_status = EFI_SUCCESS;
343+ std::wstring var_name = L"test_variable";
344+ std::string set_data = "UEFI variable data string";
345+ std::string get_data;
346+
347+ efi_status = m_client->set_variable(
348+ m_common_guid,
349+ var_name,
350+ set_data,
351+ 0);
352+
353+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
354+
355+ /* Get with the data size set to zero. This is the standard way
356+ * to discover the variable size. */
357+ efi_status = m_client->get_variable(
358+ m_common_guid,
359+ var_name,
360+ get_data,
361+ 0, 0);
362+
363+ UNSIGNED_LONGLONGS_EQUAL(EFI_BUFFER_TOO_SMALL, efi_status);
364+ UNSIGNED_LONGS_EQUAL(set_data.size(), get_data.size());
365+
366+ /* Expect remove to be permitted */
367+ efi_status = m_client->remove_variable(m_common_guid, var_name);
368+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
369+}
370+
371+TEST(SmmVariableServiceTests, getVarSizeNv)
372+{
373+ efi_status_t efi_status = EFI_SUCCESS;
374+ std::wstring var_name = L"test_variable";
375+ std::string set_data = "UEFI variable data string";
376+ std::string get_data;
377+
378+ efi_status = m_client->set_variable(
379+ m_common_guid,
380+ var_name,
381+ set_data,
382+ EFI_VARIABLE_NON_VOLATILE);
383+
384+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
385+
386+ /* Get with the data size set to zero. This is the standard way
387+ * to discover the variable size. */
388+ efi_status = m_client->get_variable(
389+ m_common_guid,
390+ var_name,
391+ get_data,
392+ 0, 0);
393+
394+ UNSIGNED_LONGLONGS_EQUAL(EFI_BUFFER_TOO_SMALL, efi_status);
395+ UNSIGNED_LONGS_EQUAL(set_data.size(), get_data.size());
396+
397+ /* Expect remove to be permitted */
398+ efi_status = m_client->remove_variable(m_common_guid, var_name);
399+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
400+}
401+
402 TEST(SmmVariableServiceTests, enumerateStoreContents)
403 {
404 efi_status_t efi_status = EFI_SUCCESS;
405--
4062.17.1
407