Commit 3faa19cd authored by David Kilroy's avatar David Kilroy Committed by John W. Linville

orinoco: prevent accessing memory outside the firmware image

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: default avatarDavid Kilroy <kilroyd@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 7e57811a
...@@ -83,7 +83,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, ...@@ -83,7 +83,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
const struct firmware *fw_entry; const struct firmware *fw_entry;
const struct orinoco_fw_header *hdr; const struct orinoco_fw_header *hdr;
const unsigned char *first_block; const unsigned char *first_block;
const unsigned char *end; const void *end;
const char *firmware; const char *firmware;
const char *fw_err; const char *fw_err;
struct net_device *dev = priv->ndev; struct net_device *dev = priv->ndev;
...@@ -152,7 +152,8 @@ orinoco_dl_firmware(struct orinoco_private *priv, ...@@ -152,7 +152,8 @@ orinoco_dl_firmware(struct orinoco_private *priv,
le16_to_cpu(hdr->headersize) + le16_to_cpu(hdr->headersize) +
le32_to_cpu(hdr->pdr_offset)); 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); printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err);
if (err) if (err)
goto abort; goto abort;
...@@ -184,7 +185,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, ...@@ -184,7 +185,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
*/ */
static int static int
symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, 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) int secondary)
{ {
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
...@@ -225,9 +226,10 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, ...@@ -225,9 +226,10 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
/* Write the PDA to the adapter */ /* Write the PDA to the adapter */
if (secondary) { if (secondary) {
size_t len = hermes_blocks_length(first_block); size_t len = hermes_blocks_length(first_block, end);
ptr = first_block + len; 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); kfree(pda);
if (ret) if (ret)
return ret; return ret;
......
...@@ -71,18 +71,6 @@ ...@@ -71,18 +71,6 @@
#define BLOCK_END 0xFFFFFFFF /* Last image block */ #define BLOCK_END 0xFFFFFFFF /* Last image block */
#define TEXT_END 0x1A /* End of text header */ #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. /* Limit the amout we try to download in a single shot.
* Size is in bytes. * Size is in bytes.
*/ */
...@@ -218,13 +206,14 @@ hermes_aux_control(hermes_t *hw, int enabled) ...@@ -218,13 +206,14 @@ hermes_aux_control(hermes_t *hw, int enabled)
* Scan PDR for the record with the specified RECORD_ID. * Scan PDR for the record with the specified RECORD_ID.
* If it's not found, return NULL. * If it's not found, return NULL.
*/ */
static struct pdr * static const struct pdr *
hermes_find_pdr(struct pdr *first_pdr, u32 record_id) hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
{ {
struct pdr *pdr = first_pdr; const struct pdr *pdr = first_pdr;
void *end = (void *)first_pdr + MAX_PDA_SIZE;
while (((void *)pdr < end) && end -= sizeof(struct pdr);
while (((void *) pdr <= end) &&
(pdr_id(pdr) != PDI_END)) { (pdr_id(pdr) != PDI_END)) {
/* /*
* PDR area is currently not terminated by 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) ...@@ -244,12 +233,15 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
} }
/* Scan production data items for a particular entry */ /* Scan production data items for a particular entry */
static struct pdi * static const struct pdi *
hermes_find_pdi(struct pdi *first_pdi, u32 record_id) 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 the record ID matches, we are done */
if (pdi_id(pdi) == record_id) if (pdi_id(pdi) == record_id)
...@@ -262,12 +254,13 @@ hermes_find_pdi(struct pdi *first_pdi, u32 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 */ /* Process one Plug Data Item - find corresponding PDR and plug it */
static int 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 */ /* 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 */ /* No match is found, safe to ignore */
if (!pdr) if (!pdr)
...@@ -345,18 +338,22 @@ int hermes_read_pda(hermes_t *hw, ...@@ -345,18 +338,22 @@ int hermes_read_pda(hermes_t *hw,
*/ */
int hermes_apply_pda(hermes_t *hw, int hermes_apply_pda(hermes_t *hw,
const char *first_pdr, const char *first_pdr,
const __le16 *pda) const void *pdr_end,
const __le16 *pda,
const void *pda_end)
{ {
int ret; int ret;
const struct pdi *pdi; 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 */ /* Go through every PDI and plug them into the adapter */
pdi = (const struct pdi *) (pda + 2); pdi = (const struct pdi *) (pda + 2);
while (pdi_id(pdi) != PDI_END) { while (((void *) pdi <= pda_end) &&
ret = hermes_plug_pdi(hw, pdr, pdi); (pdi_id(pdi) != PDI_END)) {
ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
if (ret) if (ret)
return ret; return ret;
...@@ -370,15 +367,18 @@ int hermes_apply_pda(hermes_t *hw, ...@@ -370,15 +367,18 @@ int hermes_apply_pda(hermes_t *hw,
* including the header data. * including the header data.
*/ */
size_t 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; const struct dblock *blk = (const struct dblock *) first_block;
int total_len = 0; int total_len = 0;
int len; int len;
end -= sizeof(*blk);
/* Skip all blocks to locate Plug Data References /* Skip all blocks to locate Plug Data References
* (Spectrum CS) */ * (Spectrum CS) */
while (dblock_addr(blk) != BLOCK_END) { while (((void *) blk <= end) &&
(dblock_addr(blk) != BLOCK_END)) {
len = dblock_len(blk); len = dblock_len(blk);
total_len += sizeof(*blk) + len; total_len += sizeof(*blk) + len;
blk = (struct dblock *) &blk->data[len]; blk = (struct dblock *) &blk->data[len];
...@@ -476,7 +476,7 @@ int hermesi_program_end(hermes_t *hw) ...@@ -476,7 +476,7 @@ int hermesi_program_end(hermes_t *hw)
} }
/* Program the data blocks */ /* 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; const struct dblock *blk;
u32 blkaddr; u32 blkaddr;
...@@ -488,14 +488,14 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end) ...@@ -488,14 +488,14 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end)
blk = (const struct dblock *) first_block; blk = (const struct dblock *) first_block;
if ((const char *) blk > (end - sizeof(*blk))) if ((void *) blk > (end - sizeof(*blk)))
return -EIO; return -EIO;
blkaddr = dblock_addr(blk); blkaddr = dblock_addr(blk);
blklen = dblock_len(blk); blklen = dblock_len(blk);
while ((blkaddr != BLOCK_END) && while ((blkaddr != BLOCK_END) &&
(((const char *) blk + blklen) <= end)) { (((void *) blk + blklen) <= end)) {
printk(KERN_DEBUG PFX printk(KERN_DEBUG PFX
"Programming block of length %d to address 0x%08x\n", "Programming block of length %d to address 0x%08x\n",
blklen, blkaddr); blklen, blkaddr);
...@@ -527,7 +527,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end) ...@@ -527,7 +527,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const char *end)
#endif #endif
blk = (const struct dblock *) &blk->data[blklen]; blk = (const struct dblock *) &blk->data[blklen];
if ((const char *) blk > (end - sizeof(*blk))) if ((void *) blk > (end - sizeof(*blk)))
return -EIO; return -EIO;
blkaddr = dblock_addr(blk); blkaddr = dblock_addr(blk);
...@@ -616,17 +616,20 @@ DEFINE_DEFAULT_PDR(0x0161, 256, ...@@ -616,17 +616,20 @@ DEFINE_DEFAULT_PDR(0x0161, 256,
*/ */
int hermes_apply_pda_with_defaults(hermes_t *hw, int hermes_apply_pda_with_defaults(hermes_t *hw,
const char *first_pdr, 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; const struct pdr *pdr = (const struct pdr *) first_pdr;
struct pdi *first_pdi = (struct pdi *) &pda[2]; const struct pdi *first_pdi = (const struct pdi *) &pda[2];
struct pdi *pdi; const struct pdi *pdi;
struct pdi *default_pdi = NULL; const struct pdi *default_pdi = NULL;
struct pdi *outdoor_pdi; const struct pdi *outdoor_pdi;
void *end = (void *)first_pdr + MAX_PDA_SIZE;
int record_id; int record_id;
while (((void *)pdr < end) && pdr_end -= sizeof(struct pdr);
while (((void *) pdr <= pdr_end) &&
(pdr_id(pdr) != PDI_END)) { (pdr_id(pdr) != PDI_END)) {
/* /*
* For spectrum_cs firmwares, * For spectrum_cs firmwares,
...@@ -638,7 +641,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, ...@@ -638,7 +641,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
break; break;
record_id = pdr_id(pdr); 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) if (pdi)
printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n", printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
record_id, pdi); record_id, pdi);
...@@ -646,7 +649,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, ...@@ -646,7 +649,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
switch (record_id) { switch (record_id) {
case 0x110: /* Modem REFDAC values */ case 0x110: /* Modem REFDAC values */
case 0x120: /* Modem VGDAC 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; default_pdi = NULL;
if (outdoor_pdi) { if (outdoor_pdi) {
pdi = outdoor_pdi; pdi = outdoor_pdi;
...@@ -687,7 +691,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, ...@@ -687,7 +691,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
if (pdi) { if (pdi) {
/* Lengths of the data in PDI and PDR must match */ /* 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 */ /* do the actual plugging */
hermes_aux_setaddr(hw, pdr_addr(pdr)); hermes_aux_setaddr(hw, pdr_addr(pdr));
hermes_write_bytes(hw, HERMES_AUXDATA, hermes_write_bytes(hw, HERMES_AUXDATA,
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
int hermesi_program_init(hermes_t *hw, u32 offset); int hermesi_program_init(hermes_t *hw, u32 offset);
int hermesi_program_end(hermes_t *hw); 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, int hermes_read_pda(hermes_t *hw,
__le16 *pda, __le16 *pda,
...@@ -38,11 +38,15 @@ int hermes_read_pda(hermes_t *hw, ...@@ -38,11 +38,15 @@ int hermes_read_pda(hermes_t *hw,
int use_eeprom); int use_eeprom);
int hermes_apply_pda(hermes_t *hw, int hermes_apply_pda(hermes_t *hw,
const char *first_pdr, 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, int hermes_apply_pda_with_defaults(hermes_t *hw,
const char *first_pdr, 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 */ #endif /* _HERMES_DLD_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment