mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
ocs_fc: Fix config space bounds, double PRLI response and a stack overflow
Fix three unrelated problems found by security review of the driver.
ECD helper ioctl:
- OCS_ECD_HELPER_CFG_READ* and OCS_ECD_HELPER_CFG_WRITE* passed a user
supplied offset straight to pci_read_config() and pci_write_config().
Reject accesses that extend past PCIE_REGMAX, so that a wide access
starting in range cannot run past the end of config space.
- Use the real access width (1, 2 or 4 bytes) of the command for both
the BAR and the config space checks, rather than assuming 4 bytes, so
that narrow accesses at the very end of the BAR or config space are
not rejected while wider ones remain bounded.
PRLI:
- When a PRLI carries an FC-4 type other than the one the driver
supports, ocs_d_send_prli_rsp() sent an LS_RJT and started node
shutdown, but then fell through and sent a second response on the same
IO (another LS_RJT, or a PRLI LS_ACC when the initiator is accepted),
followed by a conflicting transition to __ocs_d_device_ready. The two
responses posted two WQEs for one IO and caused a double ocs_ref_put(),
a use after free of the ELS IO. Return after dispatching the LS_RJT so
that exactly one response is sent.
- __ocs_d_port_logged_in calls ocs_d_send_prli_rsp() without checking
the FC-4 type, so any port that has completed a PLOGI can trigger this
with a single PRLI, whether or not target mode is enabled.
Management:
- The active profile id is a uint32_t that is formatted as "0x%02x"
with sprintf() into a buffer of MAX_PROFILE_LENGTH (5) bytes, which
overflows for any id above 0xff. Size the buffer for the longest
possible string ("0x" plus 8 hex digits and the terminating NUL) and
use ocs_snprintf().
- Use ocs_snprintf() for nv_wwpn as well, as nv_wwnn already does.
MFC after: 1 week
Sponsored by: Broadcom
Reviewed by: ken
This commit is contained in:
1 parent
26081296b2
commit
039a8e11f8
3 files changed
+48
-4
No files matched your search
@@ -74,6 +74,8 @@ ocs_d_send_prli_rsp(ocs_io_t *io, uint16_t ox_id)
|
||||
FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL);
|
||||
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
|
||||
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
|
||||
/* Send only one response per PRLI; the LS_RJT owns the IO */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the back-end doesn't want to talk to this initiator, we send an LS_RJT */
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
#include <sys/firmware.h>
|
||||
#include <sys/priv.h>
|
||||
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
static d_open_t ocs_open;
|
||||
static d_close_t ocs_close;
|
||||
static d_ioctl_t ocs_ioctl;
|
||||
@@ -299,6 +301,28 @@ no_support:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return the access width in bytes of an ECD helper BAR/config command
|
||||
*/
|
||||
static uint32_t
|
||||
ocs_ecd_helper_width(ocs_ecd_helper_cmd_t cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case OCS_ECD_HELPER_BAR_READ8:
|
||||
case OCS_ECD_HELPER_BAR_WRITE8:
|
||||
case OCS_ECD_HELPER_CFG_READ8:
|
||||
case OCS_ECD_HELPER_CFG_WRITE8:
|
||||
return (sizeof(uint8_t));
|
||||
case OCS_ECD_HELPER_BAR_READ16:
|
||||
case OCS_ECD_HELPER_BAR_WRITE16:
|
||||
case OCS_ECD_HELPER_CFG_READ16:
|
||||
case OCS_ECD_HELPER_CFG_WRITE16:
|
||||
return (sizeof(uint16_t));
|
||||
default:
|
||||
return (sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief perform requested Elx CoreDump helper function
|
||||
*
|
||||
@@ -318,6 +342,7 @@ ocs_process_ecd_helper (ocs_t *ocs, ocs_ioctl_ecd_helper_t *req)
|
||||
uint8_t v8;
|
||||
uint16_t v16;
|
||||
uint32_t v32;
|
||||
uint32_t width;
|
||||
|
||||
/* Check the BAR read/write commands for valid bar */
|
||||
switch(req->cmd) {
|
||||
@@ -327,6 +352,7 @@ ocs_process_ecd_helper (ocs_t *ocs, ocs_ioctl_ecd_helper_t *req)
|
||||
case OCS_ECD_HELPER_BAR_WRITE8:
|
||||
case OCS_ECD_HELPER_BAR_WRITE16:
|
||||
case OCS_ECD_HELPER_BAR_WRITE32:
|
||||
width = ocs_ecd_helper_width(req->cmd);
|
||||
if (req->bar >= PCI_MAX_BAR) {
|
||||
device_printf(ocs->dev, "Error: bar %d out of range\n", req->bar);
|
||||
return -EFAULT;
|
||||
@@ -335,13 +361,27 @@ ocs_process_ecd_helper (ocs_t *ocs, ocs_ioctl_ecd_helper_t *req)
|
||||
device_printf(ocs->dev, "Error: bar %d not defined\n", req->bar);
|
||||
return -EFAULT;
|
||||
}
|
||||
if ((uint64_t)req->offset + sizeof(uint32_t) >
|
||||
if ((uint64_t)req->offset + width >
|
||||
rman_get_size(ocs->reg[req->bar].res)) {
|
||||
device_printf(ocs->dev, "Error: bar %d offset 0x%x out of range\n",
|
||||
req->bar, req->offset);
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
case OCS_ECD_HELPER_CFG_READ8:
|
||||
case OCS_ECD_HELPER_CFG_READ16:
|
||||
case OCS_ECD_HELPER_CFG_READ32:
|
||||
case OCS_ECD_HELPER_CFG_WRITE8:
|
||||
case OCS_ECD_HELPER_CFG_WRITE16:
|
||||
case OCS_ECD_HELPER_CFG_WRITE32:
|
||||
width = ocs_ecd_helper_width(req->cmd);
|
||||
if ((uint64_t)req->offset + width - 1 > PCIE_REGMAX) {
|
||||
device_printf(ocs->dev,
|
||||
"Error: config offset 0x%x out of range\n",
|
||||
req->offset);
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2076,7 +2076,8 @@ ocs_mgmt_get_active_profile_cb(int32_t status, uint32_t active_profile, void *ul
|
||||
ocs_sem_v(&(result->semaphore));
|
||||
}
|
||||
|
||||
#define MAX_PROFILE_LENGTH 5
|
||||
/* "0x", up to 8 hex digits and a NUL for a uint32_t id */
|
||||
#define MAX_PROFILE_LENGTH 11
|
||||
|
||||
/**
|
||||
* @brief Get active profile
|
||||
@@ -2107,7 +2108,7 @@ get_active_profile(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
||||
}
|
||||
if (result.status == 0) {
|
||||
/* Success. */
|
||||
sprintf(result_string, "0x%02x", result.active_profile_id);
|
||||
ocs_snprintf(result_string, sizeof(result_string), "0x%02x", result.active_profile_id);
|
||||
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "active_profile", result_string);
|
||||
} else {
|
||||
ocs_log_test(ocs, "getting active profile status 0x%x\n", result.status);
|
||||
@@ -2225,7 +2226,8 @@ get_nv_wwpn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
||||
}
|
||||
if (result.status == 0) {
|
||||
/* Success. Copy wwpn from result struct to result string */
|
||||
sprintf(result_string, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
ocs_snprintf(result_string, sizeof(result_string),
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
result.wwpn[0], result.wwpn[1], result.wwpn[2],
|
||||
result.wwpn[3], result.wwpn[4], result.wwpn[5],
|
||||
result.wwpn[6], result.wwpn[7]);
|
||||
|
||||
Reference in new issue
Block a user