[PATCH] lib: sbi_dbtr: fix shared memory double-fetch in install_trig
liutong <[email protected]> Tue, 23 Jun 2026 18:02:25 +0800
| Newsgroups | org.infradead.lists.opensbi |
|---|---|
| Message-ID | <[email protected]> |
sbi_dbtr_install_trig() reads trigger configuration from S-mode shared memory in two separate loops: first to validate, then to install. Since the shared memory remains writable by S-mode between the two reads, the data used for installation may differ from what was validated. Fix this by taking a snapshot of the shared memory into a local buffer and performing both validation and installation against that snapshot. Also add a bounds check on trig_count against RV_MAX_TRIGGERS to prevent out-of-bounds access on the local buffer. Additionally, fix a redundant read in dbtr_trigger_setup() where tdata1 was read from the message pointer a second time instead of using the value already saved in trig->tdata1, which could lead to inconsistency between the CSR write and the trigger state setup. Signed-off-by: liutong <[email protected]> --- lib/sbi/sbi_dbtr.c | 52 +++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 01047969..8bad2d95 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -327,7 +327,7 @@ static void dbtr_trigger_setup(struct sbi_dbtr_trigger *trig, trig->tdata2 = lle_to_cpu(recv->tdata2); trig->tdata3 = lle_to_cpu(recv->tdata3); - tdata1 = lle_to_cpu(recv->tdata1); + tdata1 = trig->tdata1; trig->state = 0; @@ -603,14 +603,15 @@ int sbi_dbtr_read_trig(unsigned long smode, int sbi_dbtr_install_trig(unsigned long smode, unsigned long trig_count, unsigned long *out) { + struct sbi_dbtr_data_msg local_data[RV_MAX_TRIGGERS]; void *shmem_base = NULL; union sbi_dbtr_shmem_entry *entry; - struct sbi_dbtr_data_msg *recv; struct sbi_dbtr_id_msg *xmit; unsigned long ctrl; struct sbi_dbtr_trigger *trig; struct sbi_dbtr_hart_triggers_state *hs = NULL; bool tdata2_impl, tdata3_impl; + int i; hs = dbtr_thishart_state_ptr(); if (!hs) @@ -619,9 +620,22 @@ int sbi_dbtr_install_trig(unsigned long smode, if (sbi_dbtr_shmem_disabled(hs)) return SBI_ERR_NO_SHMEM; + if (trig_count > RV_MAX_TRIGGERS) + return SBI_ERR_INVALID_PARAM; + shmem_base = hart_shmem_base(hs); + + /* + * Snapshot trigger data from shared memory into a local buffer so + * that S-mode cannot modify the data between validation and use. + */ sbi_hart_protection_map_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); + for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { + local_data[_idx] = entry->data; + } + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); /* * SBI v3.0 sec 19.4 requires SBI_ERR_NOT_SUPPORTED when a trigger @@ -632,42 +646,35 @@ int sbi_dbtr_install_trig(unsigned long smode, tdata2_impl = tdata_implemented(CSR_TDATA2); tdata3_impl = tdata_implemented(CSR_TDATA3); - /* Check requested triggers configuration */ - for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { - recv = (struct sbi_dbtr_data_msg *)(&entry->data); - ctrl = recv->tdata1; + /* Validate triggers from the local snapshot */ + for (i = 0; i < (int)trig_count; i++) { + ctrl = local_data[i].tdata1; if (!dbtr_trigger_supported(TDATA1_GET_TYPE(ctrl))) { - *out = _idx; - sbi_hart_protection_unmap_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); + *out = i; return SBI_ERR_FAILED; } if (!dbtr_trigger_valid(TDATA1_GET_TYPE(ctrl), ctrl)) { - *out = _idx; - sbi_hart_protection_unmap_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); + *out = i; return SBI_ERR_FAILED; } - if ((recv->tdata2 && !tdata2_impl) || - (recv->tdata3 && !tdata3_impl)) { - *out = _idx; - sbi_hart_protection_unmap_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); + if ((local_data[i].tdata2 && !tdata2_impl) || + (local_data[i].tdata3 && !tdata3_impl)) { + *out = i; return SBI_ERR_NOT_SUPPORTED; } } if (hs->available_trigs < trig_count) { *out = hs->available_trigs; - sbi_hart_protection_unmap_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); return SBI_ERR_FAILED; } - /* Install triggers */ + /* Install triggers from the validated local snapshot */ + sbi_hart_protection_map_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { /* * Since we have already checked if enough triggers are @@ -675,15 +682,12 @@ int sbi_dbtr_install_trig(unsigned long smode, */ trig = sbi_alloc_trigger(); - recv = (struct sbi_dbtr_data_msg *)(&entry->data); xmit = (struct sbi_dbtr_id_msg *)(&entry->id); - dbtr_trigger_setup(trig, recv); + dbtr_trigger_setup(trig, &local_data[_idx]); dbtr_trigger_enable(trig); xmit->idx = cpu_to_lle(trig->index); - } - sbi_hart_protection_unmap_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); -- 2.34.1 -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi