Skip to content

FlatArray<T> will SEGFAULT when referencing a Toasted array #2350

Description

@mhov

In v0.18.0, when using the new FlatArray type for zero-copy arrays, if the array is Toasted data, even just referencing it in a parameter will cause a segfault. I have a failing test for replication here https://github.com/mhov/pgx/tree/flat_arr_toast_fail

cargo test --features pg18,pg_test --no-default-features borrow_test_arr_data_ptr_toasted
    #[pg_extern]
    fn borrow_get_arr_nelems(arr: &FlatArray<'_, i32>) -> libc::c_int {
        arr.nelems() as _
    }

    #[pg_test]
    fn borrow_test_arr_data_ptr_toasted() {
        // 2000 i32s should be the TOAST threshold.
        Spi::run("CREATE TEMP TABLE test_array (arr integer[])")
            .expect("failed to create temp table");
        Spi::run("INSERT INTO test_array SELECT array_agg(i) FROM generate_series(1, 2500) i")
            .expect("failed to insert into temp table");
        let len = Spi::get_one::<i32>("SELECT borrow_get_arr_nelems(arr) FROM test_array");
        assert_eq!(len, Ok(Some(2500)));
    }

This will fix it, but I'm 0% confident in the soundness of de-toasting here. As always I'd defer to @workingjubilee

unsafe impl<T: ?Sized> BorrowDatum for FlatArray<'_, T> {
    const PASS: layout::PassBy = layout::PassBy::Ref;
    unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
        unsafe {
+            let toast = ptr::NonNull::new(pg_sys::pg_detoast_datum(ptr.as_ptr().cast())).unwrap();

            let len =
-                varlena::varsize_any(ptr.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>();
+                varlena::varsize_any(toast.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>();
            ptr::NonNull::new_unchecked(
-                ptr::slice_from_raw_parts_mut(ptr.as_ptr(), len) as *mut Self
+                ptr::slice_from_raw_parts_mut(toast.as_ptr(), len) as *mut Self
            )
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions