lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4625b434-ed73-d266-0340-76c8bd8fd46e@marcan.st>
Date:   Fri, 7 Jan 2022 12:53:24 +0900
From:   Hector Martin <marcan@...can.st>
To:     Andy Shevchenko <andy.shevchenko@...il.com>
Cc:     Kalle Valo <kvalo@...eaurora.org>,
        "David S. Miller" <davem@...emloft.net>,
        Jakub Kicinski <kuba@...nel.org>,
        Rob Herring <robh+dt@...nel.org>,
        "Rafael J. Wysocki" <rafael@...nel.org>,
        Len Brown <lenb@...nel.org>,
        Arend van Spriel <aspriel@...il.com>,
        Franky Lin <franky.lin@...adcom.com>,
        Hante Meuleman <hante.meuleman@...adcom.com>,
        Chi-hsien Lin <chi-hsien.lin@...ineon.com>,
        Wright Feng <wright.feng@...ineon.com>,
        Dmitry Osipenko <digetx@...il.com>,
        Sven Peter <sven@...npeter.dev>,
        Alyssa Rosenzweig <alyssa@...enzweig.io>,
        Mark Kettenis <kettenis@...nbsd.org>,
        Rafał Miłecki <zajec5@...il.com>,
        Pieter-Paul Giesberts <pieter-paul.giesberts@...adcom.com>,
        Linus Walleij <linus.walleij@...aro.org>,
        Hans de Goede <hdegoede@...hat.com>,
        "John W. Linville" <linville@...driver.com>,
        "brian m. carlson" <sandals@...stytoothpaste.net>,
        "open list:TI WILINK WIRELES..." <linux-wireless@...r.kernel.org>,
        netdev <netdev@...r.kernel.org>,
        devicetree <devicetree@...r.kernel.org>,
        Linux Kernel Mailing List <linux-kernel@...r.kernel.org>,
        ACPI Devel Maling List <linux-acpi@...r.kernel.org>,
        "open list:BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER" 
        <brcm80211-dev-list.pdl@...adcom.com>,
        SHA-cyfmac-dev-list@...ineon.com
Subject: Re: [PATCH v2 07/35] brcmfmac: pcie: Read Apple OTP information

On 2022/01/04 20:26, Andy Shevchenko wrote:
> On Tue, Jan 4, 2022 at 9:28 AM Hector Martin <marcan@...can.st> wrote:
>>
>> On Apple platforms, the One Time Programmable ROM in the Broadcom chips
>> contains information about the specific board design (module, vendor,
>> version) that is required to select the correct NVRAM file. Parse this
>> OTP ROM and extract the required strings.
>>
>> Note that the user OTP offset/size is per-chip. This patch does not add
>> any chips yet.
> 
> ...
> 
>> +static int
>> +brcmf_pcie_parse_otp_sys_vendor(struct brcmf_pciedev_info *devinfo,
>> +                               u8 *data, size_t size)
>> +{
>> +       int idx = 4;
> 
> Can you rather have a structure
> 
> struct my_cool_and_strange_blob {
> __le32 hdr;
> const char ...[];
> ...
> }
> 
> and then cast your data to this struct?

That would mean I need to copy, since the original data is not aligned.
Since it's just one u32 header (which we don't even truly know the
interpretation of), it seems get_unaligned_le32 is easier.

> 
>> +       const char *chip_params;
>> +       const char *board_params;
>> +       const char *p;
>> +
>> +       /* 4-byte header and two empty strings */
>> +       if (size < 6)
>> +               return -EINVAL;
>> +
>> +       if (get_unaligned_le32(data) != BRCMF_OTP_VENDOR_HDR)
>> +               return -EINVAL;
>> +
>> +       chip_params = &data[idx];
> 
>> +       /* Skip first string, including terminator */
>> +       idx += strnlen(chip_params, size - idx) + 1;
> 
> strsep() ?

We're splitting on \0 here, so that won't work.

> 
>> +       if (idx >= size)
>> +               return -EINVAL;
>> +
>> +       board_params = &data[idx];
>> +
>> +       /* Skip to terminator of second string */
>> +       idx += strnlen(board_params, size - idx);
>> +       if (idx >= size)
>> +               return -EINVAL;
>> +
>> +       /* At this point both strings are guaranteed NUL-terminated */
>> +       brcmf_dbg(PCIE, "OTP: chip_params='%s' board_params='%s'\n",
>> +                 chip_params, board_params);
>> +
>> +       p = board_params;
>> +       while (*p) {
>> +               char tag = *p++;
>> +               const char *end;
>> +               size_t len;
>> +
>> +               if (tag == ' ') /* Skip extra spaces */
>> +                       continue;
> 
> skip_spaces()

Sure.

> 
>> +
>> +               if (*p++ != '=') /* implicit NUL check */
>> +                       return -EINVAL;
> 
> Have you checked the next_arg() implementation?

That function has a lot more logic (handling quotes, etc) and no other
hardware drivers use it. I'm not sure I feel comfortable using it to
parse untrusted data from a potentially compromised device. The parsing
we need to do here is much simpler.

> 
>> +               /* *p might be NUL here, if so end == p and len == 0 */
>> +               end = strchrnul(p, ' ');
>> +               len = end - p;
>> +
>> +               /* leave 1 byte for NUL in destination string */
>> +               if (len > (BRCMF_OTP_MAX_PARAM_LEN - 1))
>> +                       return -EINVAL;
>> +
>> +               /* Copy len characters plus a NUL terminator */
>> +               switch (tag) {
>> +               case 'M':
>> +                       strscpy(devinfo->otp.module, p, len + 1);
>> +                       break;
>> +               case 'V':
>> +                       strscpy(devinfo->otp.vendor, p, len + 1);
>> +                       break;
>> +               case 'm':
>> +                       strscpy(devinfo->otp.version, p, len + 1);
>> +                       break;
>> +               }
>> +
>> +               /* Skip to space separator or NUL */
>> +               p = end;
>> +       }
>> +
>> +       brcmf_dbg(PCIE, "OTP: module=%s vendor=%s version=%s\n",
>> +                 devinfo->otp.module, devinfo->otp.vendor,
>> +                 devinfo->otp.version);
>> +
>> +       if (!devinfo->otp.module ||
>> +           !devinfo->otp.vendor ||
>> +           !devinfo->otp.version)
>> +               return -EINVAL;
>> +
>> +       devinfo->otp.valid = true;
>> +       return 0;
>> +}
>> +
>> +static int
>> +brcmf_pcie_parse_otp(struct brcmf_pciedev_info *devinfo, u8 *otp, size_t size)
>> +{
>> +       int p = 0;
> 
>> +       int ret = -1;
> 
> Use proper error codes.

Ack.

> 
>> +       brcmf_dbg(PCIE, "parse_otp size=%ld\n", size);
>> +
>> +       while (p < (size - 1)) {
> 
> too many parentheses

Really? I see this is all over kernel code. I know it's redundant, but I
find parentheses around expressions used for one side of a comparison to
be a lot more readable since you don't have to start doubting whether
that particular operator has higher precedence than the comparison (+
does but & does not).

> 
>> +               u8 type = otp[p];
>> +               u8 length = otp[p + 1];
>> +
>> +               if (type == 0)
>> +                       break;
>> +
>> +               if ((p + 2 + length) > size)
>> +                       break;
>> +
>> +               switch (type) {
>> +               case BRCMF_OTP_SYS_VENDOR:
>> +                       brcmf_dbg(PCIE, "OTP @ 0x%x (0x%x): SYS_VENDOR\n",
> 
> length as hex a bit harder to parse

Not so sure about that, especially if you're trying to mentally add it
to offsets... but sure, I can make it decimal.

> 
>> +                                 p, length);
>> +                       ret = brcmf_pcie_parse_otp_sys_vendor(devinfo,
>> +                                                             &otp[p + 2],
>> +                                                             length);
>> +                       break;
>> +               case BRCMF_OTP_BRCM_CIS:
>> +                       brcmf_dbg(PCIE, "OTP @ 0x%x (0x%x): BRCM_CIS\n",
>> +                                 p, length);
>> +                       break;
>> +               default:
>> +                       brcmf_dbg(PCIE, "OTP @ 0x%x (0x%x): Unknown type 0x%x\n",
>> +                                 p, length, type);
>> +                       break;
>> +               }
> 
>> +               p += 2 + length;
> 
> 
> length + 2 is easier to read.

I was following the data order here; 2 header bytes and then length
payload bytes. Same reason I used p + 2 + length above.

> 
>> +       }
>> +
>> +       return ret;
>> +}
> 
> ...
> 
>> +               /* Map OTP to shadow area */
>> +               WRITECC32(devinfo, sromcontrol,
>> +                         sromctl | BCMA_CC_SROM_CONTROL_OTPSEL);
> 
> One line?

That exceeds 80 chars, which seems to be the standard in this file which
I'm trying to stick to. If people are okay with pushing to 100 lines,
there are lots of other places I could unwrap lines in this series.

> 
> ...
> 
>> +       otp = kzalloc(sizeof(u16) * words, GFP_KERNEL);
> 
> No check, why? I see in many places you forgot to check for NULL from
> allocator functions.

I think at some point something convinced me that kzalloc and friends
don't fail with GFP_KERNEL... which they rarely do, but they do. I'll
fix it, and add a few missing checks to the existing code while I'm at it.

> Moreover here you should use kzalloc() which does overflow protection.
words is a constant from the switch statement so this could never
overflow anyway, but sure.

-- 
Hector Martin (marcan@...can.st)
Public Key: https://mrcn.st/pub

Powered by blists - more mailing lists