Re: [PATCH v3] usb: gadget: ether: Fix MAC address parsing

From: Stefan Agner
Date: Mon May 18 2015 - 07:42:15 EST


On 2015-05-18 11:24, Krzysztof Opasiak wrote:
> Hello,
>
> On 05/14/2015 06:50 PM, Stefan Agner wrote:
>> MAC addresses can be written without leading zeros. A popular
>> example is libc's ether_ntoa_r function which creates such
>> MAC addresses.
>>
>> Example:
>> 00:14:3d:0f:ff:fe can be written as 0:14:3d:f:ff:fe
>>
>> The function get_ether_addr potentially also parsed past the
>> end of the user provided string. Use the opportunity and fix
>> the function to never parse beyond the end of the string while
>> allowing MAC addresses with and without leading zeros. Also
>> corner cases such as 00:14:3d:0f:ff:0 + new-line character
>> are parsed correctly.
>>
>> Signed-off-by: Stefan Agner <stefan@xxxxxxxx>
>> ---
>> Changes since v2:
>> - Fix parameter description
>> - Return with error if invalid charaters are part of the string
>>
>> drivers/usb/gadget/function/u_ether.c | 70 ++++++++++++++++++++++++++++-------
>> 1 file changed, 56 insertions(+), 14 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
>> index f1fd777..c71ab21 100644
>> --- a/drivers/usb/gadget/function/u_ether.c
>> +++ b/drivers/usb/gadget/function/u_ether.c
>> @@ -703,25 +703,62 @@ static int eth_stop(struct net_device *net)
>>
>> /*-------------------------------------------------------------------------*/
>>
>> +/**
>> + * get_ether_addr - parse ethernet address from string
>> + * @str: string to parse
>> + * @dev_addr: a buffer in which the parsed ethernet address will be
>> + * written
>> + *
>> + * Several common formats are supported:
>> + * 1) 00:14:3d:0f:ff:fe (no skipped 0, semicolons)
>> + * 2) 00.14.3d.0f.ff.fe (no skipped 0, dots)
>> + * 3) 00143d0ffffe (no skipped 0, no separator)
>> + * 4) 0:14:3d:f:ff:fe (skipped leading 0, semicolons)
>> + * 5) 0.14.3d.f.ff.fe (skipped leading 0, dots)
>> + *
>> + * The function will not cross the end of the provided string even
>> + * when the string has the wrong format.
>> + *
>> + * Returns 0 on success, or -EINVAL on error
>> + */
>> static int get_ether_addr(const char *str, u8 *dev_addr)
>> {
>> - if (str) {
>> - unsigned i;
>> + int num, i;
>>
>> - for (i = 0; i < 6; i++) {
>> - unsigned char num;
>> + for (i = 0; i < ETH_ALEN; i++) {
>> + int j = 0;
>>
>> - if ((*str == '.') || (*str == ':'))
>> + dev_addr[i] = 0;
>> + while (*str) {
>> + char c = *str;
>> +
>> + if (c == '.' || c == ':') {
>> str++;
>> - num = hex_to_bin(*str++) << 4;
>> - num |= hex_to_bin(*str++);
>> - dev_addr [i] = num;
>> + break;
>> + }
>> +
>> + if (j >= 2)
>> + break;
>> +
>> + /* Ignore newline character, e.g. when using echo */
>> + if (c == '\n')
>> + continue;
>
> Unfortunately this line causes livelock if user will place newline
> character in the middle of MAC address. For example:
>
> echo -e "0.14.3d.f\n\n\n.f\nf.fe" > host_addr
>
> When you find \n character you are going once again through loop but
> you are not moving to next character so c is still \n and will never
> change to anything else as it is incremented in very last line of this
> loop.
>
> In my opinion we should accept \n only at the end of input.
>
>> +
>> + num = hex_to_bin(c);
>> + if (num < 0)
>> + return num;
>> +
>> + dev_addr[i] <<= 4;
>> + dev_addr[i] |= num;
>> + j++;
>> + str++;
>> }
>
> I'm also not sure if it is a good idea to place 0 in mac address if
> string is too short. For example:
>
> echo -n 0.14.3d.f.ff
>
> which is too short MAC address (last byte is missing) will be parsed
> as 00:14:3d:0f:ff:00 and used as correct MAC address. If this is the
> way it should be, maybe place at least a comment about it as it was
> not obvious (at least for me) that too short mac address should be
> also accepted.

Hm, that worked before in a totally unclean way, since the old
implementation just parsed past the end of the string. Hence random
bytes could have appeared. I think we really should fail if the MAC
address provided is too short. Will update this.

--
Stefan
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/