Find_option() is used to retrieve the block size value in an option
acknowledgment in response to a request containing a block size option
according to RFC2348.

The format of an OACK response is described in RFC2347 as

+-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
|  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
+-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+

The current implementation of find_option() only works if

* blksize is the first option
* lwip_strnstr() ignores the length parameter,
  i.e. is implemented via strstr()

The OACK messages starts with  0x00 0x06. If 'blksize' is the first option,
strstr() reports a match when the first parameter points to 0x06. Adding
the string length of 'blksize' plus 2 to the location of the 0x06 byte
points to the value.

Find_option() would report a match for option 'blksize' if the response
contained an option called 'foo_blksize_bar'. In this case find_option()
would return 'bar' as the value string.

If 'blksize' were the second option, find_option() would return a pointer
to the second character of the value string.

Furthermore find_option() does not detect if the value string is NUL
terminated. This may lead to a buffer overrun.

Provide an implementation that correctly steps from option to option.

Fixes: 27d7ccda94fa ("net: lwip: tftp: add support of blksize option to client")
Signed-off-by: Heinrich Schuchardt <heinrich.schucha...@canonical.com>
---
v2:
        new patch
---
 lib/lwip/lwip/src/apps/tftp/tftp.c | 54 +++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 9 deletions(-)

diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c 
b/lib/lwip/lwip/src/apps/tftp/tftp.c
index 56aeabc4d73..e85e3623066 100644
--- a/lib/lwip/lwip/src/apps/tftp/tftp.c
+++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
@@ -264,19 +264,55 @@ static u16_t payload_size(void)
   return TFTP_DEFAULT_BLOCK_SIZE;
 }
 
+/**
+ * find_option() - check if OACK message contains option
+ *
+ * @p:         message buffer
+ * @option:    option key
+ * Return:     option value
+ */
 static const char *
 find_option(struct pbuf *p, const char *option)
 {
-  int i;
-  u16_t optlen = strlen(option);
-  const char *b = p->payload;
-
-  for (i = 0; i + optlen + 1 < p->len; i++) {
-    if (lwip_strnstr(b + i, option, optlen))
-      return b + i + optlen + 2;
-  }
+       const char *pos = p->payload;
+       int rem = p->len;
+
+       /*
+        * According to RFC 2347 the OACK packet has the following format:
+        *
+        * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+        * |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
+        * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+        */
+
+       /* Skip opc */
+       pos += 2;
+       rem -= 2;
+       if (rem <= 0)
+               return NULL;
+
+       for (;;) {
+               int len;
+               int match;
+
+               len = strnlen(pos, rem) + 1;
+               if (rem < len)
+                       break;
+               match = strcmp(pos, option);
+               /* Skip option */
+               pos += len;
+               rem -= len;
+               len = strnlen(pos, rem) + 1;
+               if (rem < len)
+                       break;
+               if (!match)
+                       return pos;
+               /* Skip value */
+               pos += len;
+               rem -= len;
+       }
 
-  return NULL;
+       return NULL;
 }
 
 static void
-- 
2.47.1

Reply via email to