rusticl: correct calculation of maximum allocation size

This verifies that the requested allocation doesn't exceed the maximum
in cases where the size passed to `clSVMAlloc()` isn't a multiple of the
provided alignment. It also clamps the maximum allocation to `i32::MAX`,
which prevents overflowing `pipe_box`'s `width` field.

Both of these changes prevent possible undefined behavior on 32-bit
systems due to violation of `Layout` prerequisites.

v2: use safe layout creation for maintainability, add a few comments
v3: use Layout utils for aligned size calc, split out max alloc changes
v4: use `checked_compare()` for alloc/size comparison

Reviewed-by: Karol Herbst <kherbst@redhat.com>
Cc: stable
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34166>
This commit is contained in:
Seán de Búrca 2025-03-20 13:43:48 -07:00 committed by Marge Bot
parent 7c88a52350
commit 5b1088220e

View file

@ -2330,7 +2330,7 @@ pub fn svm_alloc(
context: cl_context,
flags: cl_svm_mem_flags,
size: usize,
mut alignment: cl_uint,
alignment: cl_uint,
) -> CLResult<*mut c_void> {
// clSVMAlloc will fail if
@ -2347,29 +2347,33 @@ pub fn svm_alloc(
return Err(CL_INVALID_VALUE);
}
// size is 0 or > CL_DEVICE_MAX_MEM_ALLOC_SIZE value for any device in context.
if size == 0 || checked_compare(size, Ordering::Greater, c.max_mem_alloc()) {
let alignment = if alignment != 0 {
alignment as usize
} else {
// When alignment is 0, the size of the largest supported type is used.
// In the case of the full profile, that's `long16`.
mem::size_of::<[u64; 16]>()
};
// clSVMAlloc will fail if alignment is not a power of two.
// `from_size_align()` verifies this condition is met.
let layout = Layout::from_size_align(size, alignment).or(Err(CL_INVALID_VALUE))?;
// clSVMAlloc will fail if size is 0 or > CL_DEVICE_MAX_MEM_ALLOC_SIZE value
// for any device in context.
// Verify that the requested size, once adjusted to be a multiple of
// alignment, fits within the maximum allocation size. While
// `from_size_align()` ensures that the allocation will fit in host memory,
// the maximum allocation may be smaller due to limitations from gallium or
// devices.
let size_aligned = layout.pad_to_align().size();
if size == 0 || checked_compare(size_aligned, Ordering::Greater, c.max_mem_alloc()) {
return Err(CL_INVALID_VALUE);
}
if alignment == 0 {
alignment = mem::size_of::<[u64; 16]>() as cl_uint;
}
// alignment is not a power of two
if !alignment.is_power_of_two() {
return Err(CL_INVALID_VALUE);
}
let layout;
let ptr;
// SAFETY: we already verify the parameters to from_size_align above and layout is of non zero
// size
unsafe {
layout = Layout::from_size_align_unchecked(size, alignment as usize);
ptr = alloc::alloc(layout);
}
// SAFETY: `size` is verified to be non-zero and the returned pointer is not
// expected to point to initialized memory.
let ptr = unsafe { alloc::alloc(layout) };
if ptr.is_null() {
return Err(CL_OUT_OF_HOST_MEMORY);