Blame SOURCES/0473-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch

d18179
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
d18179
From: Daniel Axtens <dja@axtens.net>
d18179
Date: Mon, 20 Sep 2021 01:12:24 +1000
d18179
Subject: [PATCH] net/tftp: Prevent a UAF and double-free from a failed seek
d18179
d18179
A malicious tftp server can cause UAFs and a double free.
d18179
d18179
An attempt to read from a network file is handled by grub_net_fs_read(). If
d18179
the read is at an offset other than the current offset, grub_net_seek_real()
d18179
is invoked.
d18179
d18179
In grub_net_seek_real(), if a backwards seek cannot be satisfied from the
d18179
currently received packets, and the underlying transport does not provide
d18179
a seek method, then grub_net_seek_real() will close and reopen the network
d18179
protocol layer.
d18179
d18179
For tftp, the ->close() call goes to tftp_close() and frees the tftp_data_t
d18179
file->data. The file->data pointer is not nulled out after the free.
d18179
d18179
If the ->open() call fails, the file->data will not be reallocated and will
d18179
continue point to a freed memory block. This could happen from a server
d18179
refusing to send the requisite ack to the new tftp request, for example.
d18179
d18179
The seek and the read will then fail, but the grub_file continues to exist:
d18179
the failed seek does not necessarily cause the entire file to be thrown
d18179
away (e.g. where the file is checked to see if it is gzipped/lzio/xz/etc.,
d18179
a read failure is interpreted as a decompressor passing on the file, not as
d18179
an invalidation of the entire grub_file_t structure).
d18179
d18179
This means subsequent attempts to read or seek the file will use the old
d18179
file->data after free. Eventually, the file will be close()d again and
d18179
file->data will be freed again.
d18179
d18179
Mark a net_fs file that doesn't reopen as broken. Do not permit read() or
d18179
close() on a broken file (seek is not exposed directly to the file API -
d18179
it is only called as part of read, so this blocks seeks as well).
d18179
d18179
As an additional defence, null out the ->data pointer if tftp_open() fails.
d18179
That would have lead to a simple null pointer dereference rather than
d18179
a mess of UAFs.
d18179
d18179
This may affect other protocols, I haven't checked.
d18179
d18179
Signed-off-by: Daniel Axtens <dja@axtens.net>
d18179
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
d18179
(cherry picked from commit dada1dda695439bb55b2848dddc2d89843552f81)
d18179
(cherry picked from commit 352c5ae8a9fc715712e6ecbd7ccb6218122c748f)
d18179
(cherry picked from commit 61a010085ab9f0ecf42677773a6fc212f1579b0a)
d18179
(cherry picked from commit 14e15ef7b027c39528b19bfe69145c29f9795566)
d18179
---
d18179
 grub-core/net/net.c  | 11 +++++++++--
d18179
 grub-core/net/tftp.c |  1 +
d18179
 include/grub/net.h   |  1 +
d18179
 3 files changed, 11 insertions(+), 2 deletions(-)
d18179
d18179
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
d18179
index 1f62aa10f2..7e7f79143c 100644
d18179
--- a/grub-core/net/net.c
d18179
+++ b/grub-core/net/net.c
d18179
@@ -1590,7 +1590,8 @@ grub_net_fs_close (grub_file_t file)
d18179
       grub_netbuff_free (file->device->net->packs.first->nb);
d18179
       grub_net_remove_packet (file->device->net->packs.first);
d18179
     }
d18179
-  file->device->net->protocol->close (file);
d18179
+  if (!file->device->net->broken)
d18179
+    file->device->net->protocol->close (file);
d18179
   grub_free (file->device->net->name);
d18179
   return GRUB_ERR_NONE;
d18179
 }
d18179
@@ -1811,7 +1812,10 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
d18179
     file->device->net->eof = 0;
d18179
     err = file->device->net->protocol->open (file, file->device->net->name);
d18179
     if (err)
d18179
-      return err;
d18179
+      {
d18179
+	file->device->net->broken = 1;
d18179
+	return err;
d18179
+      }
d18179
     grub_net_fs_read_real (file, NULL, offset);
d18179
     return grub_errno;
d18179
   }
d18179
@@ -1820,6 +1824,9 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
d18179
 static grub_ssize_t
d18179
 grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len)
d18179
 {
d18179
+  if (file->device->net->broken)
d18179
+    return -1;
d18179
+
d18179
   if (file->offset != file->device->net->offset)
d18179
     {
d18179
       grub_err_t err;
d18179
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
d18179
index aa0424dcee..85be965470 100644
d18179
--- a/grub-core/net/tftp.c
d18179
+++ b/grub-core/net/tftp.c
d18179
@@ -402,6 +402,7 @@ tftp_open (struct grub_file *file, const char *filename)
d18179
     {
d18179
       grub_net_udp_close (data->sock);
d18179
       grub_free (data);
d18179
+      file->data = NULL;
d18179
       return grub_errno;
d18179
     }
d18179
 
d18179
diff --git a/include/grub/net.h b/include/grub/net.h
d18179
index 1ec827b9bf..50578c3ce3 100644
d18179
--- a/include/grub/net.h
d18179
+++ b/include/grub/net.h
d18179
@@ -268,6 +268,7 @@ typedef struct grub_net
d18179
   grub_fs_t fs;
d18179
   int eof;
d18179
   int stall;
d18179
+  int broken;
d18179
 } *grub_net_t;
d18179
 
d18179
 extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name);