diff --git a/ChangeLog b/ChangeLog index c891a32fc..39541adb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -70,6 +70,13 @@ * include/grub/misc.h: Remove strncat. * grub-core/lib/posix_wrap/string.h: Likewise. +2013-10-26 Vladimir Serbinenko + + * grub-core/net/tftp.c: Retransmit ack when rereceiving old packet. + Try to handle more than 0xFFFF packets. + Reported by: Bernhard Übelacker . + He also spotted few overflows in first version of this patch. + 2013-10-26 Vladimir Serbinenko * tests/date_unit_test.c: New test. diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index b9d9549c8..97217d23d 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -102,13 +102,24 @@ typedef struct tftp_data grub_uint64_t file_size; grub_uint64_t block; grub_uint32_t block_size; - grub_uint32_t ack_sent; + grub_uint64_t ack_sent; int have_oack; struct grub_error_saved save_err; grub_net_udp_socket_t sock; grub_priority_queue_t pq; } *tftp_data_t; +static int +cmp_block (grub_uint16_t a, grub_uint16_t b) +{ + grub_int16_t i = (grub_int16_t) (a - b); + if (i > 0) + return +1; + if (i < 0) + return -1; + return 0; +} + static int cmp (const void *a__, const void *b__) { @@ -117,15 +128,11 @@ cmp (const void *a__, const void *b__) struct tftphdr *a = (struct tftphdr *) a_->data; struct tftphdr *b = (struct tftphdr *) b_->data; /* We want the first elements to be on top. */ - if (grub_be_to_cpu16 (a->u.data.block) < grub_be_to_cpu16 (b->u.data.block)) - return +1; - if (grub_be_to_cpu16 (a->u.data.block) > grub_be_to_cpu16 (b->u.data.block)) - return -1; - return 0; + return -cmp_block (grub_be_to_cpu16 (a->u.data.block), grub_be_to_cpu16 (b->u.data.block)); } static grub_err_t -ack (tftp_data_t data, grub_uint16_t block) +ack (tftp_data_t data, grub_uint64_t block) { struct tftphdr *tftph_ack; grub_uint8_t nbdata[512]; @@ -213,12 +220,13 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), return GRUB_ERR_NONE; nb_top = *nb_top_p; tftph = (struct tftphdr *) nb_top->data; - if (grub_be_to_cpu16 (tftph->u.data.block) >= data->block + 1) + if (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) >= 0) break; + ack (data, grub_be_to_cpu16 (tftph->u.data.block)); grub_netbuff_free (nb_top); grub_priority_queue_pop (data->pq); } - if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1) + while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0) { unsigned size;