Skip to content

Commit f4be988

Browse files
geomatsiPaul Walmsley
authored andcommitted
riscv: ptrace: validate input vector csr registers
Add strict validation for vector csr registers when setting them via ptrace: - reject attempts to set reserved bits or invalid field combinations - enforce strict VL checks against calculated VLMAX values Vector specs 0.7.1 and 1.0 allow normal applications to set candidate VL values and read back the hardware-adjusted results, see section 6 for details. Disallow such flexibility in vector ptrace operations and strictly enforce valid VL input. The traced process may not update its saved vector context if no vector instructions execute between breakpoints. So the purpose of the strict ptrace approach is to make sure that debuggers maintain an accurate view of the tracee's vector context across multiple halt/resume debug cycles. Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com> Reviewed-by: Andy Chiu <andybnac@gmail.com> Tested-by: Andy Chiu <andybnac@gmail.com> Link: https://patch.msgid.link/20251214163537.1054292-5-geomatsi@gmail.com Signed-off-by: Paul Walmsley <pjw@kernel.org>
1 parent fd515e0 commit f4be988

1 file changed

Lines changed: 87 additions & 1 deletion

File tree

arch/riscv/kernel/ptrace.c

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,92 @@ static int riscv_vr_get(struct task_struct *target,
128128
return membuf_write(&to, vstate->datap, riscv_v_vsize);
129129
}
130130

131+
static int invalid_ptrace_v_csr(struct __riscv_v_ext_state *vstate,
132+
struct __riscv_v_regset_state *ptrace)
133+
{
134+
unsigned long vsew, vlmul, vfrac, vl;
135+
unsigned long elen, vlen;
136+
unsigned long sew, lmul;
137+
unsigned long reserved;
138+
139+
vlen = vstate->vlenb * 8;
140+
if (vstate->vlenb != ptrace->vlenb)
141+
return 1;
142+
143+
/* do not allow to set vcsr/vxrm/vxsat reserved bits */
144+
reserved = ~(CSR_VXSAT_MASK | (CSR_VXRM_MASK << CSR_VXRM_SHIFT));
145+
if (ptrace->vcsr & reserved)
146+
return 1;
147+
148+
if (has_vector()) {
149+
/* do not allow to set vtype reserved bits and vill bit */
150+
reserved = ~(VTYPE_VSEW | VTYPE_VLMUL | VTYPE_VMA | VTYPE_VTA);
151+
if (ptrace->vtype & reserved)
152+
return 1;
153+
154+
elen = riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE64X) ? 64 : 32;
155+
vsew = (ptrace->vtype & VTYPE_VSEW) >> VTYPE_VSEW_SHIFT;
156+
sew = 8 << vsew;
157+
158+
if (sew > elen)
159+
return 1;
160+
161+
vfrac = (ptrace->vtype & VTYPE_VLMUL_FRAC);
162+
vlmul = (ptrace->vtype & VTYPE_VLMUL);
163+
164+
/* RVV 1.0 spec 3.4.2: VLMUL(0x4) reserved */
165+
if (vlmul == 4)
166+
return 1;
167+
168+
/* RVV 1.0 spec 3.4.2: (LMUL < SEW_min / ELEN) reserved */
169+
if (vlmul == 5 && elen == 32)
170+
return 1;
171+
172+
/* for zero vl verify that at least one element is possible */
173+
vl = ptrace->vl ? ptrace->vl : 1;
174+
175+
if (vfrac) {
176+
/* integer 1/LMUL: VL =< VLMAX = VLEN / SEW / LMUL */
177+
lmul = 2 << (3 - (vlmul - vfrac));
178+
if (vlen < vl * sew * lmul)
179+
return 1;
180+
} else {
181+
/* integer LMUL: VL =< VLMAX = LMUL * VLEN / SEW */
182+
lmul = 1 << vlmul;
183+
if (vl * sew > lmul * vlen)
184+
return 1;
185+
}
186+
}
187+
188+
if (has_xtheadvector()) {
189+
/* do not allow to set vtype reserved bits and vill bit */
190+
reserved = ~(VTYPE_VSEW_THEAD | VTYPE_VLMUL_THEAD | VTYPE_VEDIV_THEAD);
191+
if (ptrace->vtype & reserved)
192+
return 1;
193+
194+
/*
195+
* THead ISA Extension spec chapter 16:
196+
* divided element extension ('Zvediv') is not part of XTheadVector
197+
*/
198+
if (ptrace->vtype & VTYPE_VEDIV_THEAD)
199+
return 1;
200+
201+
vsew = (ptrace->vtype & VTYPE_VSEW_THEAD) >> VTYPE_VSEW_THEAD_SHIFT;
202+
sew = 8 << vsew;
203+
204+
vlmul = (ptrace->vtype & VTYPE_VLMUL_THEAD);
205+
lmul = 1 << vlmul;
206+
207+
/* for zero vl verify that at least one element is possible */
208+
vl = ptrace->vl ? ptrace->vl : 1;
209+
210+
if (vl * sew > lmul * vlen)
211+
return 1;
212+
}
213+
214+
return 0;
215+
}
216+
131217
static int riscv_vr_set(struct task_struct *target,
132218
const struct user_regset *regset,
133219
unsigned int pos, unsigned int count,
@@ -149,7 +235,7 @@ static int riscv_vr_set(struct task_struct *target,
149235
if (unlikely(ret))
150236
return ret;
151237

152-
if (vstate->vlenb != ptrace_vstate.vlenb)
238+
if (invalid_ptrace_v_csr(vstate, &ptrace_vstate))
153239
return -EINVAL;
154240

155241
vstate->vstart = ptrace_vstate.vstart;

0 commit comments

Comments
 (0)