diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2012-02-20 21:57:33 -0500 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2012-02-22 15:48:08 -0500 |
commit | 01cb543db8a2d2dbcea5f357365ce1327ac46193 (patch) | |
tree | 34f19534fcc79f1e2616424fa26fda2851918335 /drivers/net/ethernet | |
parent | c3cb2a874122889c036934294dbbb8ad82f53060 (diff) |
sfc: Correct validation of peer_page_count in efx_vfdi_set_status_page()
efx_vfdi_set_status_page() validates the peer page count by
calculating the size of a request containing that many addresses and
comparing that with the maximum valid request size (4KB). The
calculation involves a multiplication that may overflow on a 32-bit
system.
We use kcalloc() to allocate memory to store the addresses; that also
does a multiplication and it does check for integer overflow, so any
values larger than 0x1fffffff will be rejected. However, values in
the range [0x1fffffffc, 0x1fffffff] pass boh tests and result in an
attempt to allocate nearly 4GB on the heap. This should be rejected
rather quickly as it's obviously impossible on a 32-bit system, and
indeed the maximum possible heap allocation is 32MB. Still, let's
make absolutely sure by fixing the initial validation.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/sfc/siena_sriov.c | 11 |
1 files changed, 6 insertions, 5 deletions
diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index 5c6839ec3a83..80976e84eee6 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c | |||
@@ -796,12 +796,13 @@ static int efx_vfdi_set_status_page(struct efx_vf *vf) | |||
796 | { | 796 | { |
797 | struct efx_nic *efx = vf->efx; | 797 | struct efx_nic *efx = vf->efx; |
798 | struct vfdi_req *req = vf->buf.addr; | 798 | struct vfdi_req *req = vf->buf.addr; |
799 | unsigned int page_count; | 799 | u64 page_count = req->u.set_status_page.peer_page_count; |
800 | u64 max_page_count = | ||
801 | (EFX_PAGE_SIZE - | ||
802 | offsetof(struct vfdi_req, u.set_status_page.peer_page_addr[0])) | ||
803 | / sizeof(req->u.set_status_page.peer_page_addr[0]); | ||
800 | 804 | ||
801 | page_count = req->u.set_status_page.peer_page_count; | 805 | if (!req->u.set_status_page.dma_addr || page_count > max_page_count) { |
802 | if (!req->u.set_status_page.dma_addr || EFX_PAGE_SIZE < | ||
803 | offsetof(struct vfdi_req, | ||
804 | u.set_status_page.peer_page_addr[page_count])) { | ||
805 | if (net_ratelimit()) | 806 | if (net_ratelimit()) |
806 | netif_err(efx, hw, efx->net_dev, | 807 | netif_err(efx, hw, efx->net_dev, |
807 | "ERROR: Invalid SET_STATUS_PAGE from %s\n", | 808 | "ERROR: Invalid SET_STATUS_PAGE from %s\n", |