Hi,

This patch fixes some serious bugs and memory leaks in the network
stack. It also contains two optimization:

1, use default block size of 4096. block size is configurable via
tftp_block_size environment variable.
2, reuse netbuff in efinet driver when there is no data, this saves
some alloc/free loops.

-- 
Best wishes
Bean
=== modified file 'grub-core/net/drivers/efi/efinet.c'
--- grub-core/net/drivers/efi/efinet.c  2012-03-10 19:41:28 +0000
+++ grub-core/net/drivers/efi/efinet.c  2012-05-03 18:48:18 +0000
@@ -54,6 +54,8 @@
     }
 }
 
+struct grub_net_buff *saved_nb;
+
 static struct grub_net_buff *
 get_card_packet (const struct grub_net_card *dev)
 {
@@ -63,18 +65,23 @@
   grub_efi_uintn_t bufsize = 1536;
   struct grub_net_buff *nb;
 
-  nb = grub_netbuff_alloc (bufsize);
-  if (!nb)
-    return NULL;
+  if (saved_nb)
+    {
+      nb = saved_nb;
+      grub_netbuff_clear (nb);
+      saved_nb = NULL;
+    }
+  else
+    {
+      nb = grub_netbuff_alloc (bufsize + 2);
+      if (!nb)
+       return NULL;
+    }
 
   /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible
-     by 4. So that IP header is aligned on 4 bytes. */
+     by 4. So that IP header is aligned on 4 bytes.
+     This can't fail because buffer is at least 2 bytes. */
   grub_netbuff_reserve (nb, 2);
-  if (!nb)
-    {
-      grub_netbuff_free (nb);
-      return NULL;
-    }
 
   st = efi_call_7 (net->receive, net, NULL, &bufsize,
                   nb->data, NULL, NULL, NULL);
@@ -84,31 +91,27 @@
 
       bufsize = ALIGN_UP (bufsize, 32);
 
-      nb = grub_netbuff_alloc (bufsize);
+      nb = grub_netbuff_alloc (bufsize + 2);
       if (!nb)
        return NULL;
 
       /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is 
divisible
-        by 4. So that IP header is aligned on 4 bytes. */
+        by 4. So that IP header is aligned on 4 bytes.
+        This can't fail because buffer is at least 2 bytes. */
       grub_netbuff_reserve (nb, 2);
-      if (!nb)
-       {
-         grub_netbuff_free (nb);
-         return NULL;
-       }
 
       st = efi_call_7 (net->receive, net, NULL, &bufsize,
                       nb->data, NULL, NULL, NULL);
     }
   if (st != GRUB_EFI_SUCCESS)
     {
-      grub_netbuff_free (nb);
+      saved_nb = nb;
       return NULL;
     }
   err = grub_netbuff_put (nb, bufsize);
   if (err)
     {
-      grub_netbuff_free (nb);
+      saved_nb = nb;
       return NULL;
     }
 
@@ -236,6 +239,9 @@
 {
   struct grub_net_card *card, *next;
 
+  if (saved_nb)
+    grub_netbuff_free (saved_nb);
+
   FOR_NET_CARDS_SAFE (card, next) 
     if (card->driver == &efidriver)
       grub_net_card_unregister (card);

=== modified file 'grub-core/net/ip.c'
--- grub-core/net/ip.c  2012-02-09 22:43:43 +0000
+++ grub-core/net/ip.c  2012-05-03 19:13:42 +0000
@@ -84,7 +84,7 @@
   grub_uint8_t proto;
   grub_uint64_t last_time;
   grub_priority_queue_t pq;
-  grub_uint8_t *asm_buffer;
+  struct grub_net_buff *asm_netbuff;
   grub_size_t total_len;
   grub_size_t cur_ptr;
   grub_uint8_t ttl;
@@ -342,8 +342,10 @@
       grub_netbuff_free (*nb);
       grub_priority_queue_pop (rsm->pq);
     }
-  grub_free (rsm->asm_buffer);
+  if (rsm->asm_netbuff)
+    grub_netbuff_free (rsm->asm_netbuff);
   grub_priority_queue_destroy (rsm->pq);
+  grub_free (rsm);
 }
 
 static void
@@ -352,12 +354,16 @@
   struct reassemble *rsm, **prev;
   grub_uint64_t limit_time = grub_get_time_ms () - 90000;
 
-  for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev)
+  for (prev = &reassembles, rsm = *prev; rsm; rsm = *prev)
     if (rsm->last_time < limit_time)
       {
        *prev = rsm->next;
        free_rsm (rsm);
       }
+    else
+      {
+       prev = &rsm->next;
+      }
 }
 
 static grub_err_t
@@ -464,7 +470,7 @@
          grub_free (rsm);
          return grub_errno;
        }
-      rsm->asm_buffer = 0;
+      rsm->asm_netbuff = 0;
       rsm->total_len = 0;
       rsm->cur_ptr = 0;
       rsm->ttl = 0xff;
@@ -483,22 +489,21 @@
       rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK)
                        + (nb->tail - nb->data));
       rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t));
-      rsm->asm_buffer = grub_zalloc (rsm->total_len);
-      if (!rsm->asm_buffer)
+      rsm->asm_netbuff = grub_netbuff_alloc (rsm->total_len);
+      if (!rsm->asm_netbuff)
        {
          *prev = rsm->next;
          free_rsm (rsm);
          return grub_errno;
        }
     }
-  if (!rsm->asm_buffer)
+  if (!rsm->asm_netbuff)
     return GRUB_ERR_NONE;
 
   while (1)
     {
       struct grub_net_buff **nb_top_p, *nb_top;
       grub_size_t copy;
-      grub_uint8_t *res;
       grub_size_t res_len;
       struct grub_net_buff *ret;
       grub_net_ip_protocol_t proto;
@@ -523,7 +528,10 @@
        }
       if (rsm->cur_ptr < (grub_size_t) 8 * (grub_be_to_cpu16 (iph->frags)
                                            & OFFSET_MASK))
-       return GRUB_ERR_NONE;
+       {
+         grub_netbuff_free (nb_top);
+         return GRUB_ERR_NONE;
+       }
 
       rsm->cur_ptr = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK)
                      + (nb_top->tail - nb_top->head));
@@ -538,31 +546,33 @@
          < copy)
        copy = rsm->total_len - 8 * (grub_be_to_cpu16 (iph->frags)
                                     & OFFSET_MASK);
-      grub_memcpy (&rsm->asm_buffer[8 * (grub_be_to_cpu16 (iph->frags)
+      grub_memcpy (&rsm->asm_netbuff->data[8 * (grub_be_to_cpu16 (iph->frags)
                                         & OFFSET_MASK)],
                   nb_top->data, copy);
 
       if ((grub_be_to_cpu16 (iph->frags) & MORE_FRAGMENTS))
-       continue;
-      
-      res = rsm->asm_buffer;
+       {
+         grub_netbuff_free (nb_top);
+         continue;
+       }
+      grub_netbuff_free (nb_top);
+
+      ret = rsm->asm_netbuff;
       proto = rsm->proto;
       src = rsm->source;
       dst = rsm->dest;
       ttl = rsm->ttl;
 
-      rsm->asm_buffer = 0;
+      rsm->asm_netbuff = 0;      
       res_len = rsm->total_len;
       *prev = rsm->next;
       free_rsm (rsm);
-      ret = grub_malloc (sizeof (*ret));
-      if (!ret)
+
+      if (grub_netbuff_put (ret, res_len))
        {
-         grub_free (res);
-         return grub_errno;
+         grub_netbuff_free (ret);
+         return GRUB_ERR_NONE;   
        }
-      ret->data = ret->head = res;
-      ret->tail = ret->end = res + res_len;
 
       source.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
       source.ipv4 = src;

=== modified file 'grub-core/net/tftp.c'
--- grub-core/net/tftp.c        2012-02-12 18:11:06 +0000
+++ grub-core/net/tftp.c        2012-05-03 19:13:03 +0000
@@ -27,6 +27,7 @@
 #include <grub/file.h>
 #include <grub/priority_queue.h>
 #include <grub/i18n.h>
+#include <grub/env.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -214,6 +215,7 @@
            tftph = (struct tftphdr *) nb_top->data;
            if (grub_be_to_cpu16 (tftph->u.data.block) >= data->block + 1)
              break;
+           grub_netbuff_free (nb_top);
            grub_priority_queue_pop (data->pq);
          }
        if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1)
@@ -248,7 +250,7 @@
            if ((nb_top->tail - nb_top->data) > 0)
              grub_net_put_packet (&file->device->net->packs, nb_top);
            else
-             grub_netbuff_free (nb);
+             grub_netbuff_free (nb_top);
          }
       }
       return GRUB_ERR_NONE;
@@ -269,7 +271,10 @@
 {
   struct grub_net_buff **nb_p;
   while ((nb_p = grub_priority_queue_top (data->pq)))
-    grub_netbuff_free (*nb_p);
+    {
+      grub_netbuff_free (*nb_p);
+      grub_priority_queue_pop (data->pq);
+    }
 
   grub_priority_queue_destroy (data->pq);
 }
@@ -288,6 +293,7 @@
   grub_err_t err;
   grub_uint8_t *nbd;
   grub_net_network_level_address_t addr;
+  const char *tftp_block_size;
 
   data = grub_zalloc (sizeof (*data));
   if (!data)
@@ -320,9 +326,12 @@
   rrqlen += grub_strlen ("blksize") + 1;
   rrq += grub_strlen ("blksize") + 1;
 
-  grub_strcpy (rrq, "1024");
-  rrqlen += grub_strlen ("1024") + 1;
-  rrq += grub_strlen ("1024") + 1;
+  tftp_block_size = grub_env_get ("tftp_block_size");
+  if (tftp_block_size == NULL)
+    tftp_block_size = "4096";
+  grub_strcpy (rrq, tftp_block_size);
+  rrqlen += grub_strlen (tftp_block_size) + 1;
+  rrq += grub_strlen (tftp_block_size) + 1;
 
   grub_strcpy (rrq, "tsize");
   rrqlen += grub_strlen ("tsize") + 1;

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to