@@ -199,6 +199,43 @@ get_space_usage(void)
199199 return total ;
200200}
201201
202+ static UINTN
203+ get_buf_avail (UINT32 nr_pages , uint8_t * comm_buf , uint8_t * ptr )
204+ {
205+ ptrdiff_t offset = (ptrdiff_t )ptr - (ptrdiff_t )comm_buf ;
206+ ptrdiff_t bufsize = (ptrdiff_t )nr_pages * PAGE_SIZE ;
207+
208+ if (offset < 0 || offset > bufsize ) {
209+ assert (0 );
210+ return 0 ;
211+ }
212+
213+ return (UINTN )(bufsize - offset );
214+ }
215+
216+ static inline uint8_t *
217+ unserialize_data_checked (UINT32 nr_pages , uint8_t * comm_buf ,
218+ uint8_t * * ptr , UINTN * len , UINTN limit )
219+ {
220+ uint8_t * data ;
221+ UINTN avail ;
222+
223+ * len = unserialize_uintn (ptr );
224+ avail = get_buf_avail (nr_pages , comm_buf , * ptr );
225+
226+ if (* len > limit || * len == 0 || * len > avail )
227+ return NULL ;
228+
229+ data = malloc (* len );
230+ if (!data )
231+ return NULL ;
232+
233+ memcpy (data , * ptr , * len );
234+ * ptr += * len ;
235+
236+ return data ;
237+ }
238+
202239/* A limited version of SetVariable for internal use. */
203240EFI_STATUS
204241internal_set_variable (const uint8_t * name , UINTN name_len , const EFI_GUID * guid ,
@@ -277,19 +314,20 @@ internal_get_variable(const uint8_t *name, UINTN name_len, const EFI_GUID *guid,
277314static void
278315do_get_variable (uint8_t * comm_buf )
279316{
280- UINT32 version ;
317+ UINT32 version , nr_pages ;
281318 uint8_t * ptr , * name ;
282319 EFI_GUID guid ;
283- UINTN name_len , data_len ;
320+ UINTN name_len , data_len , data_avail ;
284321 BOOLEAN at_runtime ;
285322 struct efi_variable * l ;
286323
287324 ptr = comm_buf ;
288- if (snoop_command (& ptr , & version , NULL , NULL ) != EFI_SUCCESS ) {
325+ if (snoop_command (& ptr , & version , & nr_pages , NULL ) != EFI_SUCCESS ) {
289326 assert (0 );
290327 return ;
291328 }
292- name = unserialize_data (& ptr , & name_len , NAME_LIMIT );
329+ name = unserialize_data_checked (nr_pages , comm_buf ,
330+ & ptr , & name_len , NAME_LIMIT );
293331 if (!name ) {
294332 serialize_result (& comm_buf , name_len == 0 ? EFI_NOT_FOUND : EFI_DEVICE_ERROR );
295333 return ;
@@ -299,6 +337,17 @@ do_get_variable(uint8_t *comm_buf)
299337 at_runtime = unserialize_boolean (& ptr );
300338
301339 ptr = comm_buf ;
340+ data_avail = get_buf_avail (nr_pages , comm_buf , ptr ) - sizeof (EFI_STATUS ) -
341+ sizeof (UINT32 );
342+ /*
343+ * DataSize is not firmware-controlled, and callers are not aware of comm
344+ * buffer size. Therefore, we can't return an error code even if DataSize is
345+ * larger than the actual available buffer size; all we can do is to limit
346+ * data_len accordingly.
347+ */
348+ if (data_len > data_avail )
349+ data_len = data_avail ;
350+
302351 l = var_list ;
303352 while (l ) {
304353 if (l -> name_len == name_len &&
@@ -1592,7 +1641,7 @@ debug_all_variables(const struct efi_variable *l)
15921641static void
15931642do_set_variable (uint8_t * comm_buf )
15941643{
1595- UINT32 version ;
1644+ UINT32 version , nr_pages ;
15961645 UINTN name_len , data_len ;
15971646 struct efi_variable * l , * prev = NULL ;
15981647 uint8_t * ptr , * name , * data ;
@@ -1604,17 +1653,19 @@ do_set_variable(uint8_t *comm_buf)
16041653 EFI_TIME timestamp ;
16051654
16061655 ptr = comm_buf ;
1607- if (snoop_command (& ptr , & version , NULL , NULL ) != EFI_SUCCESS ) {
1656+ if (snoop_command (& ptr , & version , & nr_pages , NULL ) != EFI_SUCCESS ) {
16081657 assert (0 );
16091658 return ;
16101659 }
1611- name = unserialize_data (& ptr , & name_len , NAME_LIMIT );
1660+ name = unserialize_data_checked (nr_pages , comm_buf ,
1661+ & ptr , & name_len , NAME_LIMIT );
16121662 if (!name ) {
16131663 serialize_result (& comm_buf , name_len == 0 ? EFI_INVALID_PARAMETER : EFI_DEVICE_ERROR );
16141664 return ;
16151665 }
16161666 unserialize_guid (& ptr , & guid );
1617- data = unserialize_data (& ptr , & data_len , data_limit (version ));
1667+ data = unserialize_data_checked (nr_pages , comm_buf ,
1668+ & ptr , & data_len , data_limit (version ));
16181669 if (!data && data_len ) {
16191670 serialize_result (& comm_buf ,
16201671 data_len > data_limit (version )
@@ -1928,19 +1979,21 @@ do_set_variable(uint8_t *comm_buf)
19281979static void
19291980do_get_next_variable (uint8_t * comm_buf )
19301981{
1982+ UINT32 nr_pages ;
19311983 UINTN name_len , avail_len ;
19321984 uint8_t * ptr , * name ;
19331985 struct efi_variable * l ;
19341986 EFI_GUID guid ;
19351987 BOOLEAN at_runtime ;
19361988
19371989 ptr = comm_buf ;
1938- if (snoop_command (& ptr , NULL , NULL , NULL ) != EFI_SUCCESS ) {
1990+ if (snoop_command (& ptr , NULL , & nr_pages , NULL ) != EFI_SUCCESS ) {
19391991 assert (0 );
19401992 return ;
19411993 }
19421994 avail_len = unserialize_uintn (& ptr );
1943- name = unserialize_data (& ptr , & name_len , NAME_LIMIT );
1995+ name = unserialize_data_checked (nr_pages , comm_buf ,
1996+ & ptr , & name_len , NAME_LIMIT );
19441997 if (!name && name_len ) {
19451998 serialize_result (& comm_buf , EFI_DEVICE_ERROR );
19461999 return ;
0 commit comments