[PATCH v2] lib: sbi_dbtr: fix shared memory double-fetch in install_trig
liutong <[email protected]> Sun, 28 Jun 2026 17:29:49 +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 merging validation and installation into a single pass. Each entry is copied to a local variable before use, so S-mode cannot modify the data between validation and installation. On validation failure, all previously installed triggers are rolled back. Also add a bounds check on trig_count against RV_MAX_TRIGGERS. 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. Signed-off-by: liutong <[email protected]> --- Changes in v2: - Replace full-array stack snapshot (local_data[RV_MAX_TRIGGERS], 1KB) with per-entry local copy (single struct, 32B) to reduce M-mode stack usage - Merge validation and installation into a single pass with rollback on failure, instead of two separate passes over the snapshot lib/sbi/sbi_dbtr.c | 66 ++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 8bcb4312..efd9a7e7 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -308,7 +308,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; @@ -582,12 +582,14 @@ 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; 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_trigger *installed[RV_MAX_TRIGGERS]; + int num_installed = 0; struct sbi_dbtr_hart_triggers_state *hs = NULL; hs = dbtr_thishart_state_ptr(); @@ -597,58 +599,54 @@ int sbi_dbtr_install_trig(unsigned long smode, if (sbi_dbtr_shmem_disabled(hs)) return SBI_ERR_NO_SHMEM; - shmem_base = hart_shmem_base(hs); - sbi_hart_protection_map_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); - - /* 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; - - if (!dbtr_trigger_supported(TDATA1_GET_TYPE(ctrl))) { - *out = _idx; - sbi_hart_protection_unmap_range((unsigned long)shmem_base, - trig_count * sizeof(*entry)); - 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)); - return SBI_ERR_FAILED; - } - } + if (trig_count > RV_MAX_TRIGGERS) + return SBI_ERR_INVALID_PARAM; 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 */ + shmem_base = hart_shmem_base(hs); + 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 - * available, trigger allocation must succeed. + * Snapshot one entry from shared memory so that S-mode + * cannot modify it between validation and installation. */ - trig = sbi_alloc_trigger(); + local = entry->data; + ctrl = lle_to_cpu(local.tdata1); - recv = (struct sbi_dbtr_data_msg *)(&entry->data); + if (!dbtr_trigger_supported(TDATA1_GET_TYPE(ctrl)) || + !dbtr_trigger_valid(TDATA1_GET_TYPE(ctrl), ctrl)) { + *out = _idx; + goto rollback; + } + + trig = sbi_alloc_trigger(); xmit = (struct sbi_dbtr_id_msg *)(&entry->id); - dbtr_trigger_setup(trig, recv); + dbtr_trigger_setup(trig, &local); dbtr_trigger_enable(trig); xmit->idx = cpu_to_lle(trig->index); - + installed[num_installed++] = trig; } sbi_hart_protection_unmap_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); return SBI_SUCCESS; + +rollback: + while (num_installed--) { + dbtr_trigger_clear(installed[num_installed]); + sbi_free_trigger(installed[num_installed]); + } + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_FAILED; } int sbi_dbtr_uninstall_trig(unsigned long trig_idx_base, -- 2.34.1 -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi