net: lwip: tftp: fix find_option()

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: 27d7ccda94 ("net: lwip: tftp: add support of blksize option to client")
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Jerome Forissier <jerome.forissier@linaro.org>
Tested-by: Jerome Forissier <jerome.forissier@linaro.org> (qemu_arm64_lwip)
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
This commit is contained in:
Heinrich Schuchardt 2025-02-03 10:11:59 +01:00 committed by Tom Rini
parent 9f6b9f57fe
commit 10917df17f

View file

@ -264,19 +264,55 @@ static u16_t payload_size(void)
return TFTP_DEFAULT_BLOCK_SIZE; 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 * static const char *
find_option(struct pbuf *p, const char *option) find_option(struct pbuf *p, const char *option)
{ {
int i; const char *pos = p->payload;
u16_t optlen = strlen(option); int rem = p->len;
const char *b = p->payload;
for (i = 0; i + optlen + 1 < p->len; i++) { /*
if (lwip_strnstr(b + i, option, optlen)) * According to RFC 2347 the OACK packet has the following format:
return b + i + optlen + 2; *
} * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
* | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
* +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
*/
return NULL; /* Skip opc */
pos += 2;
rem -= 2;
if (rem <= 0)
return NULL;
for (;;) {
int len;
int diff;
len = strnlen(pos, rem) + 1;
if (rem < len)
break;
diff = strcmp(pos, option);
/* Skip option */
pos += len;
rem -= len;
len = strnlen(pos, rem) + 1;
if (rem < len)
break;
if (!diff)
return pos;
/* Skip value */
pos += len;
rem -= len;
}
return NULL;
} }
static void static void