diff mbox

net: Allow mac_pton() to work on non-NULL terminated strings

Message ID 20180223201748.14328-1-stefan@the2masters.de (mailing list archive)
State New, archived
Headers show

Commit Message

Stefan Hellermann Feb. 23, 2018, 8:17 p.m. UTC
Commit 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper") crashes my
QNAP TS-209 NAS early on boot.

The boot code for the TS-209 is looping through an ext2 filesystem on a
384kB mtd partition (factory configuration put there by QNAP). There it
looks on every 1kB boundary if there is a valid MAC address. The
filesystem has a 1kB block size, so this seems to work.

On my device the MAC address is on the 37th 1kB block. But: On the 27th
block is a large file (1,5kB) without 0 bytes inside. The code in
qnap_tsx09_find_mac_addr() maps 1kB into memory (not a whole file or the
whole 384kB) and then calls qnap_tsx09_check_mac_addr() -> mac_pton() ->
strlen() on this memory block. as there is no 0 byte in the file on the
27th block, strlen() runs into bad memory and the machine panics. The old
code had no strlen().

Actually mac_pton() doesn't need to call strlen(), the following loop
catches short strings quite nicely. The strlen() seems to be an
optimization for calls to mac_pton with empty string. But this is rarely
the case and this is not a hot path. Remove it to reduce code size and
speed up calls with an not empty string.

Besides fixing the crash there is are other users interested in
this change, see https://patchwork.ozlabs.org/patch/851008/

Fixes: 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper")
Signed-off-by: Stefan Hellermann <stefan@the2masters.de>
Cc: <stable@vger.kernel.org> [4.4+]
---
 lib/net_utils.c | 4 ----
 1 file changed, 4 deletions(-)

Comments

Andrew Lunn Feb. 23, 2018, 8:27 p.m. UTC | #1
On Fri, Feb 23, 2018 at 09:17:48PM +0100, Stefan Hellermann wrote:
> Commit 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper") crashes my
> QNAP TS-209 NAS early on boot.
> 
> The boot code for the TS-209 is looping through an ext2 filesystem on a
> 384kB mtd partition (factory configuration put there by QNAP). There it
> looks on every 1kB boundary if there is a valid MAC address. The
> filesystem has a 1kB block size, so this seems to work.
> 
> On my device the MAC address is on the 37th 1kB block. But: On the 27th
> block is a large file (1,5kB) without 0 bytes inside. The code in
> qnap_tsx09_find_mac_addr() maps 1kB into memory (not a whole file or the
> whole 384kB) and then calls qnap_tsx09_check_mac_addr() -> mac_pton() ->
> strlen() on this memory block. as there is no 0 byte in the file on the
> 27th block, strlen() runs into bad memory and the machine panics. The old
> code had no strlen().
> 
> Actually mac_pton() doesn't need to call strlen(), the following loop
> catches short strings quite nicely. The strlen() seems to be an
> optimization for calls to mac_pton with empty string. But this is rarely
> the case and this is not a hot path. Remove it to reduce code size and
> speed up calls with an not empty string.
> 
> Besides fixing the crash there is are other users interested in
> this change, see https://patchwork.ozlabs.org/patch/851008/
> 
> Fixes: 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper")
> Signed-off-by: Stefan Hellermann <stefan@the2masters.de>
> Cc: <stable@vger.kernel.org> [4.4+]

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew
Alexey Dobriyan Feb. 23, 2018, 8:41 p.m. UTC | #2
On Fri, Feb 23, 2018 at 09:17:48PM +0100, Stefan Hellermann wrote:
> @@ -8,10 +8,6 @@ bool mac_pton(const char *s, u8 *mac)
>  {
>  	int i;
>  
> -	/* XX:XX:XX:XX:XX:XX */
> -	if (strlen(s) < 3 * ETH_ALEN - 1)
> -		return false;
> -
>  	/* Don't dirty result unless string is valid MAC. */
>  	for (i = 0; i < ETH_ALEN; i++) {
>  		if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1]))

Short string will bail in the loop, indeed.

Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
Andy Shevchenko Feb. 23, 2018, 8:51 p.m. UTC | #3
On Fri, 2018-02-23 at 23:41 +0300, Alexey Dobriyan wrote:
> On Fri, Feb 23, 2018 at 09:17:48PM +0100, Stefan Hellermann wrote:
> > @@ -8,10 +8,6 @@ bool mac_pton(const char *s, u8 *mac)
> >  {
> >  	int i;
> >  
> > -	/* XX:XX:XX:XX:XX:XX */
> > -	if (strlen(s) < 3 * ETH_ALEN - 1)
> > -		return false;
> > -
> >  	/* Don't dirty result unless string is valid MAC. */
> >  	for (i = 0; i < ETH_ALEN; i++) {
> >  		if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1]))
> 
> Short string will bail in the loop, indeed.
> 
> Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>

Since the author is okay with the change, I'm following:

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
David Miller Feb. 26, 2018, 6:37 p.m. UTC | #4
From: Stefan Hellermann <stefan@the2masters.de>
Date: Fri, 23 Feb 2018 21:17:48 +0100

> Commit 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper") crashes my
> QNAP TS-209 NAS early on boot.
> 
> The boot code for the TS-209 is looping through an ext2 filesystem on a
> 384kB mtd partition (factory configuration put there by QNAP). There it
> looks on every 1kB boundary if there is a valid MAC address. The
> filesystem has a 1kB block size, so this seems to work.
> 
> On my device the MAC address is on the 37th 1kB block. But: On the 27th
> block is a large file (1,5kB) without 0 bytes inside. The code in
> qnap_tsx09_find_mac_addr() maps 1kB into memory (not a whole file or the
> whole 384kB) and then calls qnap_tsx09_check_mac_addr() -> mac_pton() ->
> strlen() on this memory block. as there is no 0 byte in the file on the
> 27th block, strlen() runs into bad memory and the machine panics. The old
> code had no strlen().
> 
> Actually mac_pton() doesn't need to call strlen(), the following loop
> catches short strings quite nicely. The strlen() seems to be an
> optimization for calls to mac_pton with empty string. But this is rarely
> the case and this is not a hot path. Remove it to reduce code size and
> speed up calls with an not empty string.
> 
> Besides fixing the crash there is are other users interested in
> this change, see https://patchwork.ozlabs.org/patch/851008/
> 
> Fixes: 4904dbda41c8 ("ARM: orion5x: use mac_pton() helper")
> Signed-off-by: Stefan Hellermann <stefan@the2masters.de>
> Cc: <stable@vger.kernel.org> [4.4+]

Nope, instead the Fixes: tag commit should be completely reverted.

It is wrong on several levels.

If we don't check for NULL termination, only the caller knows if it
is safe to blindly parse the buffer and whether it's length is
legitimate, and in what manner it is ok to do so.

But that's not my main beef.

The major problem is that the orion5x code passes an __iomem pointer
to mac_pton().

It is never correct to use real C pointer dereferencing on an __iomem
pointer.  __iomem pointer accesses must go through the appropriate
iomem accessor functions and interfaces, because __iomem pointers are
opaque and not real C pointers.

sparse should have emitted a ton of warnings for the orion5x code, and
furthermore it should have done so when the __iomem pointer is passed
straight into mac_pton().

It is therefore the orion5x's code business to do such non-portable
operations, and to be able to assume that the mac buffer is safe to be
non-NULL terminated.

Therefore I am reverted the above mentioned commit to fix this
problem.

Thank you.
diff mbox

Patch

diff --git a/lib/net_utils.c b/lib/net_utils.c
index af525353395d..9d38da67ee44 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -8,10 +8,6 @@  bool mac_pton(const char *s, u8 *mac)
 {
 	int i;
 
-	/* XX:XX:XX:XX:XX:XX */
-	if (strlen(s) < 3 * ETH_ALEN - 1)
-		return false;
-
 	/* Don't dirty result unless string is valid MAC. */
 	for (i = 0; i < ETH_ALEN; i++) {
 		if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1]))