generic: platform/mikrotik: fix LZOR support
authorThibaut VARÈNE <hacks@slashdirt.org>
Sat, 16 May 2020 15:12:06 +0000 (17:12 +0200)
committerHauke Mehrtens <hauke@hauke-m.de>
Sat, 16 May 2020 18:32:20 +0000 (20:32 +0200)
31e99fe3da which introduced this code was unfortunately untested.
This commit fixes a number of issues and works around the fact that in
this particular scheme, the LZO payload may be padded at the end which
will trigger a harmless lzo decompression error.
This commit also disambiguates the debug printks.

Tested-by: Robert Marko <robimarko@gmail.com>
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
Fixes: 31e99fe3da ("generic: platform/mikrotik: support LZOR encoding")
(cherry picked from commit 2ea481193c1654c9cb42aa0331cdbc4570783e26)

target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c

index ab78037..e5257b9 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "routerboot.h"
 
-#define RB_HARDCONFIG_VER              "0.02"
+#define RB_HARDCONFIG_VER              "0.03"
 #define RB_HC_PR_PFX                   "[rb_hardconfig] "
 
 /* ID values for hardware settings */
@@ -483,16 +483,18 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
                                    void *outbuf, size_t *outlen)
 {
        u16 rle_ofs, rle_len;
-       size_t templen;
+       const u32 *needle;
        u8 *tempbuf;
+       size_t templen, lzo_len;
        int ret;
 
-       templen = inlen + sizeof(hc_lzor_prefix);
-       if (templen > *outlen)
+       lzo_len = inlen + sizeof(hc_lzor_prefix);
+       if (lzo_len > *outlen)
                return -EFBIG;
 
        /* Temporary buffer same size as the outbuf */
-       tempbuf = kmalloc(*outlen, GFP_KERNEL);
+       templen = *outlen;
+       tempbuf = kmalloc(templen, GFP_KERNEL);
        if (!outbuf)
                return -ENOMEM;
 
@@ -500,41 +502,54 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
        memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
        memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
 
-       /* LZO-decompress templen bytes of outbuf into the tempbuf */
-       ret = lzo1x_decompress_safe(outbuf, templen, tempbuf, outlen);
+       /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
+       ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
        if (ret) {
-               pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
-               goto fail;
+               if (LZO_E_INPUT_NOT_CONSUMED == ret) {
+                       /*
+                        * It is assumed that because the LZO payload is embedded
+                        * in a "root" RB_ID_WLAN_DATA tag, the tag length is aligned
+                        * and the payload is padded at the end, which triggers a
+                        * spurious error which we ignore here.
+                        */
+                       pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
+               } else {
+                       pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
+                       goto fail;
+               }
        }
-       templen = *outlen;
 
        /*
         * Post decompression we have a blob (possibly byproduct of the lzo
         * dictionary). We need to find RB_MAGIC_ERD. The magic number seems to
         * be 32bit-aligned in the decompression output.
         */
-
-       while (RB_MAGIC_ERD != *(u32 *)tempbuf) {
-               tempbuf += 4;
-               templen -= 4;
-       }
+       needle = (const u32 *)tempbuf;
+       while (RB_MAGIC_ERD != *needle++) {
+               if ((u8 *)needle >= tempbuf+templen) {
+                       pr_debug(RB_HC_PR_PFX "LZOR: ERD magic not found\n");
+                       goto fail;
+               }
+       };
+       templen -= (u8 *)needle - tempbuf;
 
        /* Past magic. Look for tag node */
-       ret = routerboot_tag_find(tempbuf, templen, 0x1, &rle_ofs, &rle_len);
+       ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len);
        if (ret) {
-               pr_debug(RB_HC_PR_PFX "RLE data not found\n");
+               pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n");
                goto fail;
        }
 
        if (rle_len > templen) {
-               pr_debug(RB_HC_PR_PFX "Invalid RLE data length\n");
+               pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
+               ret = -EINVAL;
                goto fail;
        }
 
-       /* RLE-decode tempbuf back into the outbuf */
-       ret = routerboot_rle_decode(tempbuf+rle_ofs, rle_len, outbuf, outlen);
+       /* RLE-decode tempbuf from needle back into the outbuf */
+       ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
        if (ret)
-               pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
+               pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
 
 fail:
        kfree(tempbuf);