radixdb-plugin-abi 1.2.21

Stable low-level C ABI for trusted RadixDB native plugins
Documentation
use std::process::Command;

use radixdb_plugin_abi::*;

const CHILD_ENV: &str = "RADIXDB_PLUGIN_ABI_MALFORMED_CASE";

fn package_with_types(
    types: *const RadixAbiExternalTypeDescriptorV1,
    type_count: u32,
) -> RadixPluginDescriptorV1 {
    RadixPluginDescriptorV1 {
        header: RadixAbiHeaderV1::new::<RadixPluginDescriptorV1>(RADIX_PACKAGE_CAP_EXTERNAL_TYPES),
        package_id: [1; 16],
        package_name: RadixAbiSliceV1::from_static(b"malformed"),
        package_version: RadixAbiSliceV1::from_static(b"1.0.0"),
        abi_min_minor: RADIX_ABI_MINOR,
        abi_max_minor: RADIX_ABI_MINOR,
        reserved: 0,
        descriptor_fingerprint: [2; 32],
        type_count,
        reserved_types: 0,
        types,
        function_count: 0,
        reserved_functions: 0,
        functions: std::ptr::null(),
        operator_count: 0,
        reserved_operators: 0,
        operators: std::ptr::null(),
        operator_class_count: 0,
        reserved_operator_classes: 0,
        operator_classes: std::ptr::null(),
        planner_support_count: 0,
        reserved_planner_support: 0,
        planner_support: std::ptr::null(),
    }
}

#[test]
#[ignore = "isolated child entrypoint for malformed ABI matrix"]
fn malformed_abi_child() {
    match std::env::var(CHILD_ENV).unwrap().as_str() {
        "null-slice" => assert_eq!(
            validate_slice(
                RadixAbiSliceV1 {
                    ptr: std::ptr::null(),
                    len: 1,
                    reserved: 0,
                },
                8,
                1,
            ),
            Err(RadixAbiValidationError::NullPointer)
        ),
        "misaligned-pointer" => {
            let aligned = std::ptr::NonNull::<u64>::dangling().as_ptr().cast::<u8>();
            assert_eq!(
                validate_slice(
                    RadixAbiSliceV1 {
                        ptr: aligned.wrapping_add(1),
                        len: 1,
                        reserved: 0,
                    },
                    8,
                    8,
                ),
                Err(RadixAbiValidationError::MisalignedPointer)
            );
        }
        "short-header" => assert_eq!(
            validate_header(
                &RadixAbiHeaderV1 {
                    abi_major: RADIX_ABI_MAJOR,
                    abi_minor: RADIX_ABI_MINOR,
                    struct_size: 8,
                    flags: 0,
                },
                16,
                0,
            ),
            Err(RadixAbiValidationError::StructTooSmall)
        ),
        "unknown-flag" => assert_eq!(
            validate_header(
                &RadixAbiHeaderV1 {
                    abi_major: RADIX_ABI_MAJOR,
                    abi_minor: RADIX_ABI_MINOR,
                    struct_size: 16,
                    flags: 1 << 63,
                },
                16,
                0,
            ),
            Err(RadixAbiValidationError::UnknownFlags)
        ),
        "bad-type" => assert_eq!(
            validate_type_ref(&RadixAbiTypeRefV1 {
                kind: 99,
                builtin_tag: 0,
                codec_version: 0,
                object_id: [0; 16],
            }),
            Err(RadixAbiValidationError::InvalidTypeReference)
        ),
        "missing-external-id" => assert_eq!(
            validate_type_ref(&RadixAbiTypeRefV1::external([0; 16], 1)),
            Err(RadixAbiValidationError::InvalidTypeReference)
        ),
        "oversized-slice" => assert_eq!(
            validate_slice(
                RadixAbiSliceV1 {
                    ptr: std::ptr::NonNull::<u8>::dangling().as_ptr().cast_const(),
                    len: 9,
                    reserved: 0,
                },
                8,
                1,
            ),
            Err(RadixAbiValidationError::LimitExceeded)
        ),
        "null-batch-table" => assert_eq!(
            validate_batch_view(&RadixAbiBatchViewV1 {
                header: RadixAbiHeaderV1::new::<RadixAbiBatchViewV1>(0),
                row_count: 1,
                column_count: 1,
                columns: std::ptr::null(),
            }),
            Err(RadixAbiValidationError::InvalidBatchShape)
        ),
        "unknown-result-flag" => assert_eq!(
            validate_result_item(1 << 31, 0, RadixAbiSliceV1::EMPTY, 8),
            Err(RadixAbiValidationError::UnknownFlags)
        ),
        "success-diagnostic" => assert_eq!(
            validate_diagnostic(&RadixAbiDiagnosticV1 {
                header: RadixAbiHeaderV1::new::<RadixAbiDiagnosticV1>(0),
                category: RADIX_DIAGNOSTIC_INTERNAL,
                status: RADIX_STATUS_OK,
                detail: RadixAbiSliceV1::EMPTY,
                field: RadixAbiSliceV1::EMPTY,
            }),
            Err(RadixAbiValidationError::InvalidStatus)
        ),
        "null-descriptor-table" => assert_eq!(
            validate_package_descriptor_shallow(&package_with_types(std::ptr::null(), 1)),
            Err(RadixAbiValidationError::NullPointer)
        ),
        "oversized-descriptor-table" => assert_eq!(
            validate_package_descriptor_shallow(&package_with_types(
                std::ptr::NonNull::<RadixAbiExternalTypeDescriptorV1>::dangling().as_ptr(),
                RADIX_MAX_DESCRIPTOR_ENTRIES + 1,
            )),
            Err(RadixAbiValidationError::LimitExceeded)
        ),
        other => panic!("unknown malformed case {other}"),
    }
}

#[test]
fn malformed_tag_size_flag_and_pointer_matrix_fails_closed_in_subprocesses() {
    for case in [
        "null-slice",
        "misaligned-pointer",
        "short-header",
        "unknown-flag",
        "bad-type",
        "missing-external-id",
        "oversized-slice",
        "null-batch-table",
        "unknown-result-flag",
        "success-diagnostic",
        "null-descriptor-table",
        "oversized-descriptor-table",
    ] {
        let output = Command::new(std::env::current_exe().unwrap())
            .args(["--exact", "malformed_abi_child", "--ignored", "--nocapture"])
            .env(CHILD_ENV, case)
            .output()
            .unwrap();
        assert!(
            output.status.success(),
            "malformed ABI child {case} failed:\n{}\n{}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );
    }
}