* [PATCH 1/2] fpga: altera-cvp: Avoid out-of-bounds read in trailing byte write
@ 2026-07-01 9:26 Daisuke Matsuda
2026-07-01 9:26 ` [PATCH 2/2] fpga: altera-cvp: Propagate PCI config access errors Daisuke Matsuda
0 siblings, 1 reply; 2+ messages in thread
From: Daisuke Matsuda @ 2026-07-01 9:26 UTC (permalink / raw)
To: yilun.xu, mdf, trix
Cc: linux-fpga, linux-kernel, Daisuke Matsuda, Daisuke Matsuda
From: Daisuke Matsuda <matsuda@preferred.jp>
The trailing byte path in altera_cvp_send_block() dereferences a u32
pointer even when only 1-3 bytes remain in the input buffer. If the buffer
ends at a page or scatterlist boundary, this can read past the valid image
data and fault.
Copy the remaining bytes into a zero-initialized u32 before writing the
final word so only valid bytes are read from the input buffer.
Fixes: 34d1dc17ce97 ("fpga manager: Add Altera CvP driver")
Signed-off-by: Daisuke Matsuda <matsuda@preferred.jp>
---
drivers/fpga/altera-cvp.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..c00292370a8e 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sizes.h>
+#include <linux/string.h>
#define CVP_BAR 0 /* BAR used for data transfer in memory mode */
#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */
@@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
static int altera_cvp_send_block(struct altera_cvp_conf *conf,
const u32 *data, size_t len)
{
- u32 mask, words = len / sizeof(u32);
+ u32 word, words = len / sizeof(u32);
int i, remainder;
for (i = 0; i < words; i++)
@@ -270,9 +271,9 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf,
/* write up to 3 trailing bytes, if any */
remainder = len % sizeof(u32);
if (remainder) {
- mask = BIT(remainder * 8) - 1;
- if (mask)
- conf->write_data(conf, *data & mask);
+ word = 0;
+ memcpy(&word, data, remainder);
+ conf->write_data(conf, word);
}
return 0;
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] fpga: altera-cvp: Propagate PCI config access errors
2026-07-01 9:26 [PATCH 1/2] fpga: altera-cvp: Avoid out-of-bounds read in trailing byte write Daisuke Matsuda
@ 2026-07-01 9:26 ` Daisuke Matsuda
0 siblings, 0 replies; 2+ messages in thread
From: Daisuke Matsuda @ 2026-07-01 9:26 UTC (permalink / raw)
To: yilun.xu, mdf, trix
Cc: linux-fpga, linux-kernel, Daisuke Matsuda, Daisuke Matsuda
From: Daisuke Matsuda <matsuda@preferred.jp>
The CvP control path accesses the VSEC registers through PCI config space,
but several reads and writes ignore the return value. If a config read
fails, the driver can make decisions from an undefined register value and
continue programming with bogus status or control bits.
Fixes: 34d1dc17ce97 ("fpga manager: Add Altera CvP driver")
Signed-off-by: Daisuke Matsuda <matsuda@preferred.jp>
---
drivers/fpga/altera-cvp.c | 137 +++++++++++++++++++++++++++++---------
1 file changed, 105 insertions(+), 32 deletions(-)
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index c00292370a8e..8a896a93eba8 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -82,7 +82,7 @@ struct altera_cvp_conf {
};
struct cvp_priv {
- void (*switch_clk)(struct altera_cvp_conf *conf);
+ int (*switch_clk)(struct altera_cvp_conf *conf);
int (*clear_state)(struct altera_cvp_conf *conf);
int (*wait_credit)(struct fpga_manager *mgr, u32 blocks);
size_t block_size;
@@ -116,7 +116,8 @@ static enum fpga_mgr_states altera_cvp_state(struct fpga_manager *mgr)
struct altera_cvp_conf *conf = mgr->priv;
u32 status;
- altera_read_config_dword(conf, VSE_CVP_STATUS, &status);
+ if (altera_read_config_dword(conf, VSE_CVP_STATUS, &status))
+ return FPGA_MGR_STATE_UNKNOWN;
if (status & VSE_CVP_STATUS_CFG_DONE)
return FPGA_MGR_STATE_OPERATING;
@@ -139,19 +140,27 @@ static void altera_cvp_write_data_config(struct altera_cvp_conf *conf, u32 val)
}
/* switches between CvP clock and internal clock */
-static void altera_cvp_dummy_write(struct altera_cvp_conf *conf)
+static int altera_cvp_dummy_write(struct altera_cvp_conf *conf)
{
unsigned int i;
+ int ret;
u32 val;
/* set 1 CVP clock cycle for every CVP Data Register Write */
- altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK;
val |= 1 << VSE_CVP_MODE_CTRL_NUMCLKS_OFF;
- altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ if (ret)
+ return ret;
for (i = 0; i < CVP_DUMMY_WR; i++)
conf->write_data(conf, 0); /* dummy data, could be any value */
+
+ return 0;
}
static int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask,
@@ -159,13 +168,17 @@ static int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask,
{
unsigned int retries;
u32 val;
+ int ret;
retries = timeout_us / 10;
if (timeout_us % 10)
retries++;
do {
- altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+ if (ret)
+ return ret;
+
if ((val & status_mask) == status_val)
return 0;
@@ -287,21 +300,31 @@ static int altera_cvp_teardown(struct fpga_manager *mgr,
u32 val;
/* STEP 12 - reset START_XFER bit */
- altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ if (ret)
+ return ret;
+
val &= ~VSE_CVP_PROG_CTRL_START_XFER;
- altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ if (ret)
+ return ret;
/* STEP 13 - reset CVP_CONFIG bit */
val &= ~VSE_CVP_PROG_CTRL_CONFIG;
- altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ if (ret)
+ return ret;
/*
* STEP 14
* - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy
* writes to the HIP
*/
- if (conf->priv->switch_clk)
- conf->priv->switch_clk(conf);
+ if (conf->priv->switch_clk) {
+ ret = conf->priv->switch_clk(conf);
+ if (ret)
+ return ret;
+ }
/* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */
ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0,
@@ -336,7 +359,10 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
conf->numclks = 1; /* for uncompressed and unencrypted images */
/* STEP 1 - read CVP status and check CVP_EN flag */
- altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_STATUS, &val);
+ if (ret)
+ return ret;
+
if (!(val & VSE_CVP_STATUS_CVP_EN)) {
dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val);
return -ENODEV;
@@ -354,21 +380,34 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
* - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned)
*/
/* switch from fabric to PMA clock */
- altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
val |= VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
- altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ if (ret)
+ return ret;
/* set CVP mode */
- altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
val |= VSE_CVP_MODE_CTRL_CVP_MODE;
- altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ if (ret)
+ return ret;
/*
* STEP 3
* - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
*/
- if (conf->priv->switch_clk)
- conf->priv->switch_clk(conf);
+ if (conf->priv->switch_clk) {
+ ret = conf->priv->switch_clk(conf);
+ if (ret)
+ return ret;
+ }
if (conf->priv->clear_state) {
ret = conf->priv->clear_state(conf);
@@ -381,10 +420,15 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
conf->sent_packets = 0;
/* STEP 4 - set CVP_CONFIG bit */
- altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ if (ret)
+ return ret;
+
/* request control block to begin transfer using CVP */
val |= VSE_CVP_PROG_CTRL_CONFIG;
- altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ if (ret)
+ return ret;
/* STEP 5 - poll CVP_CONFIG READY for 1 with timeout */
ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY,
@@ -399,8 +443,11 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
* STEP 6
* - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
*/
- if (conf->priv->switch_clk)
- conf->priv->switch_clk(conf);
+ if (conf->priv->switch_clk) {
+ ret = conf->priv->switch_clk(conf);
+ if (ret)
+ return ret;
+ }
if (altera_cvp_chkcfg) {
ret = altera_cvp_chk_error(mgr, 0);
@@ -411,16 +458,26 @@ static int altera_cvp_write_init(struct fpga_manager *mgr,
}
/* STEP 7 - set START_XFER */
- altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val);
+ if (ret)
+ return ret;
+
val |= VSE_CVP_PROG_CTRL_START_XFER;
- altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val);
+ if (ret)
+ return ret;
/* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */
if (conf->priv->switch_clk) {
- altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK;
val |= conf->numclks << VSE_CVP_MODE_CTRL_NUMCLKS_OFF;
- altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ if (ret)
+ return ret;
}
return 0;
}
@@ -490,17 +547,25 @@ static int altera_cvp_write_complete(struct fpga_manager *mgr,
return ret;
/* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */
- altera_read_config_dword(conf, VSE_UNCOR_ERR_STATUS, &val);
+ ret = altera_read_config_dword(conf, VSE_UNCOR_ERR_STATUS, &val);
+ if (ret)
+ return ret;
+
if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) {
dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n");
return -EPROTO;
}
/* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */
- altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ ret = altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
val &= ~VSE_CVP_MODE_CTRL_CVP_MODE;
- altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ ret = altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val);
+ if (ret)
+ return ret;
/* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */
mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE;
@@ -590,7 +655,10 @@ static int altera_cvp_probe(struct pci_dev *pdev,
return -ENODEV;
}
- pci_read_config_dword(pdev, offset + VSE_CVP_STATUS, ®val);
+ ret = pci_read_config_dword(pdev, offset + VSE_CVP_STATUS, ®val);
+ if (ret)
+ return ret;
+
if (!(regval & VSE_CVP_STATUS_CVP_EN)) {
dev_err(&pdev->dev,
"CVP is disabled for this device: CVP_STATUS Reg 0x%x\n",
@@ -613,10 +681,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
* even if the concerned BAR is not needed for FPGA configuration
* at all. Thus, enable the device via PCI config space command.
*/
- pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ ret = pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (ret)
+ return ret;
+
if (!(cmd & PCI_COMMAND_MEMORY)) {
cmd |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ ret = pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ if (ret)
+ return ret;
}
ret = pci_request_region(pdev, CVP_BAR, "CVP");
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-01 9:27 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 9:26 [PATCH 1/2] fpga: altera-cvp: Avoid out-of-bounds read in trailing byte write Daisuke Matsuda
2026-07-01 9:26 ` [PATCH 2/2] fpga: altera-cvp: Propagate PCI config access errors Daisuke Matsuda
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox