mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-07-02 22:02:01 +01:00
lib: sbi_pmu: fix integer overflow in pmu_ctr_idx_validate
pmu_ctr_idx_validate() checks whether counter indices are in range using cbase + sbi_fls(cmask) < total_ctrs. Both operands are unsigned long, so a crafted cbase close to ULONG_MAX causes the addition to wrap around to a small value that passes the comparison. Once validation is bypassed, sbi_pmu_ctr_cfg_match() with the SKIP_MATCH flag uses the overflowed index directly as an array subscript into phs->active_events[], producing an out-of-bounds read in M-mode. Through the firmware-event code path, the same overflowed index reaches fw_counters_data[] and fw_counters_started, giving an attacker OOB write-zero and OOB bit-set primitives in M-mode memory. Fix pmu_ctr_idx_validate() by checking for unsigned overflow before the comparison, and add a secondary bounds check on cidx_first in the SKIP_MATCH path so that even if validation is somehow bypassed in the future, the array access remains bounded. Signed-off-by: liutong <liutong@iscas.ac.cn> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20260624035049.1753003-1-liutong@iscas.ac.cn Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
+13
-2
@@ -223,8 +223,16 @@ static int pmu_ctr_validate(struct sbi_pmu_hart_state *phs,
|
|||||||
|
|
||||||
static bool pmu_ctr_idx_validate(unsigned long cbase, unsigned long cmask)
|
static bool pmu_ctr_idx_validate(unsigned long cbase, unsigned long cmask)
|
||||||
{
|
{
|
||||||
/* Do a basic sanity check of counter base & mask */
|
unsigned long last;
|
||||||
return cmask && cbase + sbi_fls(cmask) < total_ctrs;
|
|
||||||
|
if (!cmask)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
last = sbi_fls(cmask);
|
||||||
|
if (cbase > -1UL - last)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (cbase + last) < total_ctrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sbi_pmu_ctr_fw_read(unsigned long cidx, uint64_t *cval, bool high_bits)
|
int sbi_pmu_ctr_fw_read(unsigned long cidx, uint64_t *cval, bool high_bits)
|
||||||
@@ -915,6 +923,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
|||||||
*/
|
*/
|
||||||
unsigned long cidx_first = cidx_base + sbi_ffs(cidx_mask);
|
unsigned long cidx_first = cidx_base + sbi_ffs(cidx_mask);
|
||||||
|
|
||||||
|
if (cidx_first >= total_ctrs)
|
||||||
|
return SBI_EINVAL;
|
||||||
|
|
||||||
if (phs->active_events[cidx_first] == SBI_PMU_EVENT_IDX_INVALID)
|
if (phs->active_events[cidx_first] == SBI_PMU_EVENT_IDX_INVALID)
|
||||||
return SBI_EINVAL;
|
return SBI_EINVAL;
|
||||||
ctr_idx = cidx_first;
|
ctr_idx = cidx_first;
|
||||||
|
|||||||
Reference in New Issue
Block a user