orinoco: prevent accessing memory outside the firmware image
authorDavid Kilroy <kilroyd@googlemail.com>
Sat, 21 Feb 2009 16:52:54 +0000 (16:52 +0000)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 27 Feb 2009 19:53:02 +0000 (14:53 -0500)
Do this by indicating the end of the appropriate regions of memory.

Note that MAX_PDA_SIZE should only apply to the PDA block read from
flash/EEPROM, and has been erronously applied to the pdr elements.
Remove the macro, and use the actual PDA size passed down by the caller.

We also fix up some of the types used, marking as much as possible
const, and using void* for the end pointers.

Signed-off-by: David Kilroy <kilroyd@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/orinoco/fw.c
drivers/net/wireless/orinoco/hermes_dld.c
drivers/net/wireless/orinoco/hermes_dld.h

index 9f163feb9362d3d06dda1d3c0a6c7686c47fb995..e7abb45d6753bc5cad4aba31604d529f7caf95ee 100644 (file)
@@ -83,7 +83,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
        const struct firmware *fw_entry;
        const struct orinoco_fw_header *hdr;
        const unsigned char *first_block;
-       const unsigned char *end;
+       const void *end;
        const char *firmware;
        const char *fw_err;
        struct net_device *dev = priv->ndev;
@@ -152,7 +152,8 @@ orinoco_dl_firmware(struct orinoco_private *priv,
                       le16_to_cpu(hdr->headersize) +
                       le32_to_cpu(hdr->pdr_offset));
 
-       err = hermes_apply_pda_with_defaults(hw, first_block, pda);
+       err = hermes_apply_pda_with_defaults(hw, first_block, end, pda,
+                                            &pda[fw->pda_size / sizeof(*pda)]);
        printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err);
        if (err)
                goto abort;
@@ -184,7 +185,7 @@ free:
  */
 static int
 symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
-               const unsigned char *image, const unsigned char *end,
+               const unsigned char *image, const void *end,
                int secondary)
 {
        hermes_t *hw = &priv->hw;
@@ -225,9 +226,10 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
 
        /* Write the PDA to the adapter */
        if (secondary) {
-               size_t len = hermes_blocks_length(first_block);
+               size_t len = hermes_blocks_length(first_block, end);
                ptr = first_block + len;
-               ret = hermes_apply_pda(hw, ptr, pda);
+               ret = hermes_apply_pda(hw, ptr, end, pda,
+                                      &pda[fw->pda_size / sizeof(*pda)]);
                kfree(pda);
                if (ret)
                        return ret;
index 5260ceb5cfecae7e98244196e51041daced6c006..a9ba195cdada70963503c6b6bbd0f41ddd4c5905 100644 (file)
 #define BLOCK_END      0xFFFFFFFF      /* Last image block */
 #define TEXT_END       0x1A            /* End of text header */
 
-/*
- * PDA == Production Data Area
- *
- * In principle, the max. size of the PDA is is 4096 words. Currently,
- * however, only about 500 bytes of this area are used.
- *
- * Some USB implementations can't handle sizes in excess of 1016. Note
- * that PDA is not actually used in those USB environments, but may be
- * retrieved by common code.
- */
-#define MAX_PDA_SIZE   1000
-
 /* Limit the amout we try to download in a single shot.
  * Size is in bytes.
  */
@@ -218,13 +206,14 @@ hermes_aux_control(hermes_t *hw, int enabled)
  * Scan PDR for the record with the specified RECORD_ID.
  * If it's not found, return NULL.
  */
-static struct pdr *
-hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
+static const struct pdr *
+hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
 {
-       struct pdr *pdr = first_pdr;
-       void *end = (void *)first_pdr + MAX_PDA_SIZE;
+       const struct pdr *pdr = first_pdr;
 
-       while (((void *)pdr < end) &&
+       end -= sizeof(struct pdr);
+
+       while (((void *) pdr <= end) &&
               (pdr_id(pdr) != PDI_END)) {
                /*
                 * PDR area is currently not terminated by PDI_END.
@@ -244,12 +233,15 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
 }
 
 /* Scan production data items for a particular entry */
-static struct pdi *
-hermes_find_pdi(struct pdi *first_pdi, u32 record_id)
+static const struct pdi *
+hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
 {
-       struct pdi *pdi = first_pdi;
+       const struct pdi *pdi = first_pdi;
+
+       end -= sizeof(struct pdi);
 
-       while (pdi_id(pdi) != PDI_END) {
+       while (((void *) pdi <= end) &&
+              (pdi_id(pdi) != PDI_END)) {
 
                /* If the record ID matches, we are done */
                if (pdi_id(pdi) == record_id)
@@ -262,12 +254,13 @@ hermes_find_pdi(struct pdi *first_pdi, u32 record_id)
 
 /* Process one Plug Data Item - find corresponding PDR and plug it */
 static int
-hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
+hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
+               const struct pdi *pdi, const void *pdr_end)
 {
-       struct pdr *pdr;
+       const struct pdr *pdr;
 
        /* Find the PDR corresponding to this PDI */
-       pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
+       pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
 
        /* No match is found, safe to ignore */
        if (!pdr)
@@ -345,18 +338,22 @@ int hermes_read_pda(hermes_t *hw,
  */
 int hermes_apply_pda(hermes_t *hw,
                     const char *first_pdr,
-                    const __le16 *pda)
+                    const void *pdr_end,
+                    const __le16 *pda,
+                    const void *pda_end)
 {
        int ret;
        const struct pdi *pdi;
-       struct pdr *pdr;
+       const struct pdr *pdr;
 
-       pdr = (struct pdr *) first_pdr;
+       pdr = (const struct pdr *) first_pdr;
+       pda_end -= sizeof(struct pdi);
 
        /* Go through every PDI and plug them into the adapter */
        pdi = (const struct pdi *) (pda + 2);
-       while (pdi_id(pdi) != PDI_END) {
-               ret = hermes_plug_pdi(hw, pdr, pdi);
+       while (((void *) pdi <= pda_end) &&
+              (pdi_id(pdi) != PDI_END)) {
+               ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
                if (ret)
                        return ret;
 
@@ -370,15 +367,18 @@ int hermes_apply_pda(hermes_t *hw,
  * including the header data.
  */
 size_t
-hermes_blocks_length(const char *first_block)
+hermes_blocks_length(const char *first_block, const void *end)
 {
        const struct dblock *blk = (const struct dblock *) first_block;
        int total_len = 0;
        int len;
 
+       end -= sizeof(*blk);
+
        /* Skip all blocks to locate Plug Data References
         * (Spectrum CS) */
-       while (dblock_addr(blk) != BLOCK_END) {
+       while (((void *) blk <= end) &&
+              (dblock_addr(blk) != BLOCK_END)) {
                len = dblock_len(blk);
                total_len += sizeof(*blk) + len;
                blk = (struct dblock *) &blk->data[len];
@@ -476,7 +476,7 @@ int hermesi_program_end(hermes_t *hw)
 }
 
 /* Program the data blocks */
-int hermes_program(hermes_t *hw, const char *first_block, const char *end)
+int hermes_program(hermes_t *hw, const char *first_block, const void *end)
 {
        const struct dblock *blk;
        u32 blkaddr;
@@ -488,14 +488,14 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end)
 
        blk = (const struct dblock *) first_block;
 
-       if ((const char *) blk > (end - sizeof(*blk)))
+       if ((void *) blk > (end - sizeof(*blk)))
                return -EIO;
 
        blkaddr = dblock_addr(blk);
        blklen = dblock_len(blk);
 
        while ((blkaddr != BLOCK_END) &&
-              (((const char *) blk + blklen) <= end)) {
+              (((void *) blk + blklen) <= end)) {
                printk(KERN_DEBUG PFX
                       "Programming block of length %d to address 0x%08x\n",
                       blklen, blkaddr);
@@ -527,7 +527,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end)
 #endif
                blk = (const struct dblock *) &blk->data[blklen];
 
-               if ((const char *) blk > (end - sizeof(*blk)))
+               if ((void *) blk > (end - sizeof(*blk)))
                        return -EIO;
 
                blkaddr = dblock_addr(blk);
@@ -545,9 +545,9 @@ static const struct {                                                       \
        __le16 id;                                                      \
        u8 val[length];                                                 \
 } __attribute__ ((packed)) default_pdr_data_##pid = {                  \
-       cpu_to_le16((sizeof(default_pdr_data_##pid)/            \
+       cpu_to_le16((sizeof(default_pdr_data_##pid)/                    \
                                sizeof(__le16)) - 1),                   \
-       cpu_to_le16(pid),                                       \
+       cpu_to_le16(pid),                                               \
        data                                                            \
 }
 
@@ -616,17 +616,20 @@ DEFINE_DEFAULT_PDR(0x0161, 256,
  */
 int hermes_apply_pda_with_defaults(hermes_t *hw,
                                   const char *first_pdr,
-                                  const __le16 *pda)
+                                  const void *pdr_end,
+                                  const __le16 *pda,
+                                  const void *pda_end)
 {
        const struct pdr *pdr = (const struct pdr *) first_pdr;
-       struct pdi *first_pdi = (struct pdi *) &pda[2];
-       struct pdi *pdi;
-       struct pdi *default_pdi = NULL;
-       struct pdi *outdoor_pdi;
-       void *end = (void *)first_pdr + MAX_PDA_SIZE;
+       const struct pdi *first_pdi = (const struct pdi *) &pda[2];
+       const struct pdi *pdi;
+       const struct pdi *default_pdi = NULL;
+       const struct pdi *outdoor_pdi;
        int record_id;
 
-       while (((void *)pdr < end) &&
+       pdr_end -= sizeof(struct pdr);
+
+       while (((void *) pdr <= pdr_end) &&
               (pdr_id(pdr) != PDI_END)) {
                /*
                 * For spectrum_cs firmwares,
@@ -638,7 +641,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
                        break;
                record_id = pdr_id(pdr);
 
-               pdi = hermes_find_pdi(first_pdi, record_id);
+               pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
                if (pdi)
                        printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
                               record_id, pdi);
@@ -646,7 +649,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
                switch (record_id) {
                case 0x110: /* Modem REFDAC values */
                case 0x120: /* Modem VGDAC values */
-                       outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1);
+                       outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
+                                                     pda_end);
                        default_pdi = NULL;
                        if (outdoor_pdi) {
                                pdi = outdoor_pdi;
@@ -687,7 +691,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
 
                if (pdi) {
                        /* Lengths of the data in PDI and PDR must match */
-                       if (pdi_len(pdi) == pdr_len(pdr)) {
+                       if ((pdi_len(pdi) == pdr_len(pdr)) &&
+                           ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
                                /* do the actual plugging */
                                hermes_aux_setaddr(hw, pdr_addr(pdr));
                                hermes_write_bytes(hw, HERMES_AUXDATA,
index 6fcb262779991a63451188cc1c4d372b738a5991..583a5bcf917548236d63f6991350d8b760bcfd38 100644 (file)
@@ -29,7 +29,7 @@
 
 int hermesi_program_init(hermes_t *hw, u32 offset);
 int hermesi_program_end(hermes_t *hw);
-int hermes_program(hermes_t *hw, const char *first_block, const char *end);
+int hermes_program(hermes_t *hw, const char *first_block, const void *end);
 
 int hermes_read_pda(hermes_t *hw,
                    __le16 *pda,
@@ -38,11 +38,15 @@ int hermes_read_pda(hermes_t *hw,
                    int use_eeprom);
 int hermes_apply_pda(hermes_t *hw,
                     const char *first_pdr,
-                    const __le16 *pda);
+                    const void *pdr_end,
+                    const __le16 *pda,
+                    const void *pda_end);
 int hermes_apply_pda_with_defaults(hermes_t *hw,
                                   const char *first_pdr,
-                                  const __le16 *pda);
+                                  const void *pdr_end,
+                                  const __le16 *pda,
+                                  const void *pda_end);
 
-size_t hermes_blocks_length(const char *first_block);
+size_t hermes_blocks_length(const char *first_block, const void *end);
 
 #endif /* _HERMES_DLD_H */