xref: /OK3568_Linux_fs/buildroot/boot/grub2/0015-tftp-Do-not-use-priority-queue.patch (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593SmuzhiyunFrom b6c4a1b204740fe52b32e7f530831a59f4038e20 Mon Sep 17 00:00:00 2001
2*4882a593SmuzhiyunFrom: Alexey Makhalov <amakhalov@vmware.com>
3*4882a593SmuzhiyunDate: Thu, 9 Jul 2020 08:10:40 +0000
4*4882a593SmuzhiyunSubject: [PATCH] tftp: Do not use priority queue
5*4882a593SmuzhiyunMIME-Version: 1.0
6*4882a593SmuzhiyunContent-Type: text/plain; charset=UTF-8
7*4882a593SmuzhiyunContent-Transfer-Encoding: 8bit
8*4882a593Smuzhiyun
9*4882a593SmuzhiyunThere is not need to reassemble the order of blocks. Per RFC 1350,
10*4882a593Smuzhiyunserver must wait for the ACK, before sending next block. Data packets
11*4882a593Smuzhiyuncan be served immediately without putting them to priority queue.
12*4882a593Smuzhiyun
13*4882a593SmuzhiyunLogic to handle incoming packet is this:
14*4882a593Smuzhiyun  - if packet block id equal to expected block id, then
15*4882a593Smuzhiyun    process the packet,
16*4882a593Smuzhiyun  - if packet block id is less than expected - this is retransmit
17*4882a593Smuzhiyun    of old packet, then ACK it and drop the packet,
18*4882a593Smuzhiyun  - if packet block id is more than expected - that shouldn't
19*4882a593Smuzhiyun    happen, just drop the packet.
20*4882a593Smuzhiyun
21*4882a593SmuzhiyunIt makes the tftp receive path code simpler, smaller and faster.
22*4882a593SmuzhiyunAs a benefit, this change fixes CID# 73624 and CID# 96690, caused
23*4882a593Smuzhiyunby following while loop:
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun  while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)
26*4882a593Smuzhiyun
27*4882a593Smuzhiyunwhere tftph pointer is not moving from one iteration to another, causing
28*4882a593Smuzhiyunto serve same packet again. Luckily, double serving didn't happen due to
29*4882a593Smuzhiyundata->block++ during the first iteration.
30*4882a593Smuzhiyun
31*4882a593SmuzhiyunFixes: CID 73624, CID 96690
32*4882a593Smuzhiyun
33*4882a593SmuzhiyunSigned-off-by: Alexey Makhalov <amakhalov@vmware.com>
34*4882a593SmuzhiyunReviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
35*4882a593SmuzhiyunSigned-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
36*4882a593Smuzhiyun---
37*4882a593Smuzhiyun grub-core/net/tftp.c | 168 ++++++++++++++-----------------------------
38*4882a593Smuzhiyun 1 file changed, 53 insertions(+), 115 deletions(-)
39*4882a593Smuzhiyun
40*4882a593Smuzhiyundiff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
41*4882a593Smuzhiyunindex 7d90bf66e..b4297bc8d 100644
42*4882a593Smuzhiyun--- a/grub-core/net/tftp.c
43*4882a593Smuzhiyun+++ b/grub-core/net/tftp.c
44*4882a593Smuzhiyun@@ -25,7 +25,6 @@
45*4882a593Smuzhiyun #include <grub/mm.h>
46*4882a593Smuzhiyun #include <grub/dl.h>
47*4882a593Smuzhiyun #include <grub/file.h>
48*4882a593Smuzhiyun-#include <grub/priority_queue.h>
49*4882a593Smuzhiyun #include <grub/i18n.h>
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun GRUB_MOD_LICENSE ("GPLv3+");
52*4882a593Smuzhiyun@@ -106,31 +105,8 @@ typedef struct tftp_data
53*4882a593Smuzhiyun   int have_oack;
54*4882a593Smuzhiyun   struct grub_error_saved save_err;
55*4882a593Smuzhiyun   grub_net_udp_socket_t sock;
56*4882a593Smuzhiyun-  grub_priority_queue_t pq;
57*4882a593Smuzhiyun } *tftp_data_t;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun-static int
60*4882a593Smuzhiyun-cmp_block (grub_uint16_t a, grub_uint16_t b)
61*4882a593Smuzhiyun-{
62*4882a593Smuzhiyun-  grub_int16_t i = (grub_int16_t) (a - b);
63*4882a593Smuzhiyun-  if (i > 0)
64*4882a593Smuzhiyun-    return +1;
65*4882a593Smuzhiyun-  if (i < 0)
66*4882a593Smuzhiyun-    return -1;
67*4882a593Smuzhiyun-  return 0;
68*4882a593Smuzhiyun-}
69*4882a593Smuzhiyun-
70*4882a593Smuzhiyun-static int
71*4882a593Smuzhiyun-cmp (const void *a__, const void *b__)
72*4882a593Smuzhiyun-{
73*4882a593Smuzhiyun-  struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
74*4882a593Smuzhiyun-  struct grub_net_buff *b_ = *(struct grub_net_buff **) b__;
75*4882a593Smuzhiyun-  struct tftphdr *a = (struct tftphdr *) a_->data;
76*4882a593Smuzhiyun-  struct tftphdr *b = (struct tftphdr *) b_->data;
77*4882a593Smuzhiyun-  /* We want the first elements to be on top.  */
78*4882a593Smuzhiyun-  return -cmp_block (grub_be_to_cpu16 (a->u.data.block), grub_be_to_cpu16 (b->u.data.block));
79*4882a593Smuzhiyun-}
80*4882a593Smuzhiyun-
81*4882a593Smuzhiyun static grub_err_t
82*4882a593Smuzhiyun ack (tftp_data_t data, grub_uint64_t block)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun@@ -207,73 +183,60 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
85*4882a593Smuzhiyun 	  return GRUB_ERR_NONE;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun-      err = grub_priority_queue_push (data->pq, &nb);
89*4882a593Smuzhiyun-      if (err)
90*4882a593Smuzhiyun-	return err;
91*4882a593Smuzhiyun-
92*4882a593Smuzhiyun-      {
93*4882a593Smuzhiyun-	struct grub_net_buff **nb_top_p, *nb_top;
94*4882a593Smuzhiyun-	while (1)
95*4882a593Smuzhiyun-	  {
96*4882a593Smuzhiyun-	    nb_top_p = grub_priority_queue_top (data->pq);
97*4882a593Smuzhiyun-	    if (!nb_top_p)
98*4882a593Smuzhiyun-	      return GRUB_ERR_NONE;
99*4882a593Smuzhiyun-	    nb_top = *nb_top_p;
100*4882a593Smuzhiyun-	    tftph = (struct tftphdr *) nb_top->data;
101*4882a593Smuzhiyun-	    if (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) >= 0)
102*4882a593Smuzhiyun-	      break;
103*4882a593Smuzhiyun-	    ack (data, grub_be_to_cpu16 (tftph->u.data.block));
104*4882a593Smuzhiyun-	    grub_netbuff_free (nb_top);
105*4882a593Smuzhiyun-	    grub_priority_queue_pop (data->pq);
106*4882a593Smuzhiyun-	  }
107*4882a593Smuzhiyun-	while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)
108*4882a593Smuzhiyun-	  {
109*4882a593Smuzhiyun-	    unsigned size;
110*4882a593Smuzhiyun-
111*4882a593Smuzhiyun-	    grub_priority_queue_pop (data->pq);
112*4882a593Smuzhiyun-
113*4882a593Smuzhiyun-	    if (file->device->net->packs.count < 50)
114*4882a593Smuzhiyun+      /* Ack old/retransmitted block. */
115*4882a593Smuzhiyun+      if (grub_be_to_cpu16 (tftph->u.data.block) < data->block + 1)
116*4882a593Smuzhiyun+	ack (data, grub_be_to_cpu16 (tftph->u.data.block));
117*4882a593Smuzhiyun+      /* Ignore unexpected block. */
118*4882a593Smuzhiyun+      else if (grub_be_to_cpu16 (tftph->u.data.block) > data->block + 1)
119*4882a593Smuzhiyun+	grub_dprintf ("tftp", "TFTP unexpected block # %d\n", tftph->u.data.block);
120*4882a593Smuzhiyun+      else
121*4882a593Smuzhiyun+	{
122*4882a593Smuzhiyun+	  unsigned size;
123*4882a593Smuzhiyun+
124*4882a593Smuzhiyun+	  if (file->device->net->packs.count < 50)
125*4882a593Smuzhiyun+	    {
126*4882a593Smuzhiyun 	      err = ack (data, data->block + 1);
127*4882a593Smuzhiyun-	    else
128*4882a593Smuzhiyun-	      {
129*4882a593Smuzhiyun-		file->device->net->stall = 1;
130*4882a593Smuzhiyun-		err = 0;
131*4882a593Smuzhiyun-	      }
132*4882a593Smuzhiyun-	    if (err)
133*4882a593Smuzhiyun-	      return err;
134*4882a593Smuzhiyun-
135*4882a593Smuzhiyun-	    err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) +
136*4882a593Smuzhiyun-				     sizeof (tftph->u.data.block));
137*4882a593Smuzhiyun-	    if (err)
138*4882a593Smuzhiyun-	      return err;
139*4882a593Smuzhiyun-	    size = nb_top->tail - nb_top->data;
140*4882a593Smuzhiyun-
141*4882a593Smuzhiyun-	    data->block++;
142*4882a593Smuzhiyun-	    if (size < data->block_size)
143*4882a593Smuzhiyun-	      {
144*4882a593Smuzhiyun-		if (data->ack_sent < data->block)
145*4882a593Smuzhiyun-		  ack (data, data->block);
146*4882a593Smuzhiyun-		file->device->net->eof = 1;
147*4882a593Smuzhiyun-		file->device->net->stall = 1;
148*4882a593Smuzhiyun-		grub_net_udp_close (data->sock);
149*4882a593Smuzhiyun-		data->sock = NULL;
150*4882a593Smuzhiyun-	      }
151*4882a593Smuzhiyun-	    /* Prevent garbage in broken cards. Is it still necessary
152*4882a593Smuzhiyun-	       given that IP implementation has been fixed?
153*4882a593Smuzhiyun-	     */
154*4882a593Smuzhiyun-	    if (size > data->block_size)
155*4882a593Smuzhiyun-	      {
156*4882a593Smuzhiyun-		err = grub_netbuff_unput (nb_top, size - data->block_size);
157*4882a593Smuzhiyun-		if (err)
158*4882a593Smuzhiyun-		  return err;
159*4882a593Smuzhiyun-	      }
160*4882a593Smuzhiyun-	    /* If there is data, puts packet in socket list. */
161*4882a593Smuzhiyun-	    if ((nb_top->tail - nb_top->data) > 0)
162*4882a593Smuzhiyun-	      grub_net_put_packet (&file->device->net->packs, nb_top);
163*4882a593Smuzhiyun-	    else
164*4882a593Smuzhiyun-	      grub_netbuff_free (nb_top);
165*4882a593Smuzhiyun-	  }
166*4882a593Smuzhiyun-      }
167*4882a593Smuzhiyun+	      if (err)
168*4882a593Smuzhiyun+		return err;
169*4882a593Smuzhiyun+	    }
170*4882a593Smuzhiyun+	  else
171*4882a593Smuzhiyun+	    file->device->net->stall = 1;
172*4882a593Smuzhiyun+
173*4882a593Smuzhiyun+	  err = grub_netbuff_pull (nb, sizeof (tftph->opcode) +
174*4882a593Smuzhiyun+				   sizeof (tftph->u.data.block));
175*4882a593Smuzhiyun+	  if (err)
176*4882a593Smuzhiyun+	    return err;
177*4882a593Smuzhiyun+	  size = nb->tail - nb->data;
178*4882a593Smuzhiyun+
179*4882a593Smuzhiyun+	  data->block++;
180*4882a593Smuzhiyun+	  if (size < data->block_size)
181*4882a593Smuzhiyun+	    {
182*4882a593Smuzhiyun+	      if (data->ack_sent < data->block)
183*4882a593Smuzhiyun+		ack (data, data->block);
184*4882a593Smuzhiyun+	      file->device->net->eof = 1;
185*4882a593Smuzhiyun+	      file->device->net->stall = 1;
186*4882a593Smuzhiyun+	      grub_net_udp_close (data->sock);
187*4882a593Smuzhiyun+	      data->sock = NULL;
188*4882a593Smuzhiyun+	    }
189*4882a593Smuzhiyun+	  /*
190*4882a593Smuzhiyun+	   * Prevent garbage in broken cards. Is it still necessary
191*4882a593Smuzhiyun+	   * given that IP implementation has been fixed?
192*4882a593Smuzhiyun+	   */
193*4882a593Smuzhiyun+	  if (size > data->block_size)
194*4882a593Smuzhiyun+	    {
195*4882a593Smuzhiyun+	      err = grub_netbuff_unput (nb, size - data->block_size);
196*4882a593Smuzhiyun+	      if (err)
197*4882a593Smuzhiyun+		return err;
198*4882a593Smuzhiyun+	    }
199*4882a593Smuzhiyun+	  /* If there is data, puts packet in socket list. */
200*4882a593Smuzhiyun+	  if ((nb->tail - nb->data) > 0)
201*4882a593Smuzhiyun+	    {
202*4882a593Smuzhiyun+	      grub_net_put_packet (&file->device->net->packs, nb);
203*4882a593Smuzhiyun+	      /* Do not free nb. */
204*4882a593Smuzhiyun+	      return GRUB_ERR_NONE;
205*4882a593Smuzhiyun+	    }
206*4882a593Smuzhiyun+	}
207*4882a593Smuzhiyun+      grub_netbuff_free (nb);
208*4882a593Smuzhiyun       return GRUB_ERR_NONE;
209*4882a593Smuzhiyun     case TFTP_ERROR:
210*4882a593Smuzhiyun       data->have_oack = 1;
211*4882a593Smuzhiyun@@ -287,19 +250,6 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
212*4882a593Smuzhiyun     }
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun-static void
216*4882a593Smuzhiyun-destroy_pq (tftp_data_t data)
217*4882a593Smuzhiyun-{
218*4882a593Smuzhiyun-  struct grub_net_buff **nb_p;
219*4882a593Smuzhiyun-  while ((nb_p = grub_priority_queue_top (data->pq)))
220*4882a593Smuzhiyun-    {
221*4882a593Smuzhiyun-      grub_netbuff_free (*nb_p);
222*4882a593Smuzhiyun-      grub_priority_queue_pop (data->pq);
223*4882a593Smuzhiyun-    }
224*4882a593Smuzhiyun-
225*4882a593Smuzhiyun-  grub_priority_queue_destroy (data->pq);
226*4882a593Smuzhiyun-}
227*4882a593Smuzhiyun-
228*4882a593Smuzhiyun static grub_err_t
229*4882a593Smuzhiyun tftp_open (struct grub_file *file, const char *filename)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun@@ -372,17 +322,9 @@ tftp_open (struct grub_file *file, const char *filename)
232*4882a593Smuzhiyun   file->not_easily_seekable = 1;
233*4882a593Smuzhiyun   file->data = data;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun-  data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
236*4882a593Smuzhiyun-  if (!data->pq)
237*4882a593Smuzhiyun-    {
238*4882a593Smuzhiyun-      grub_free (data);
239*4882a593Smuzhiyun-      return grub_errno;
240*4882a593Smuzhiyun-    }
241*4882a593Smuzhiyun-
242*4882a593Smuzhiyun   err = grub_net_resolve_address (file->device->net->server, &addr);
243*4882a593Smuzhiyun   if (err)
244*4882a593Smuzhiyun     {
245*4882a593Smuzhiyun-      destroy_pq (data);
246*4882a593Smuzhiyun       grub_free (data);
247*4882a593Smuzhiyun       return err;
248*4882a593Smuzhiyun     }
249*4882a593Smuzhiyun@@ -392,7 +334,6 @@ tftp_open (struct grub_file *file, const char *filename)
250*4882a593Smuzhiyun 				  file);
251*4882a593Smuzhiyun   if (!data->sock)
252*4882a593Smuzhiyun     {
253*4882a593Smuzhiyun-      destroy_pq (data);
254*4882a593Smuzhiyun       grub_free (data);
255*4882a593Smuzhiyun       return grub_errno;
256*4882a593Smuzhiyun     }
257*4882a593Smuzhiyun@@ -406,7 +347,6 @@ tftp_open (struct grub_file *file, const char *filename)
258*4882a593Smuzhiyun       if (err)
259*4882a593Smuzhiyun 	{
260*4882a593Smuzhiyun 	  grub_net_udp_close (data->sock);
261*4882a593Smuzhiyun-	  destroy_pq (data);
262*4882a593Smuzhiyun 	  grub_free (data);
263*4882a593Smuzhiyun 	  return err;
264*4882a593Smuzhiyun 	}
265*4882a593Smuzhiyun@@ -423,7 +363,6 @@ tftp_open (struct grub_file *file, const char *filename)
266*4882a593Smuzhiyun   if (grub_errno)
267*4882a593Smuzhiyun     {
268*4882a593Smuzhiyun       grub_net_udp_close (data->sock);
269*4882a593Smuzhiyun-      destroy_pq (data);
270*4882a593Smuzhiyun       grub_free (data);
271*4882a593Smuzhiyun       return grub_errno;
272*4882a593Smuzhiyun     }
273*4882a593Smuzhiyun@@ -466,7 +405,6 @@ tftp_close (struct grub_file *file)
274*4882a593Smuzhiyun 	grub_print_error ();
275*4882a593Smuzhiyun       grub_net_udp_close (data->sock);
276*4882a593Smuzhiyun     }
277*4882a593Smuzhiyun-  destroy_pq (data);
278*4882a593Smuzhiyun   grub_free (data);
279*4882a593Smuzhiyun   return GRUB_ERR_NONE;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun--
282*4882a593Smuzhiyun2.26.2
283*4882a593Smuzhiyun
284