Чтение указателей «без происхождения» с использованием asm считается безопасным?

Недавно я написал довольно простую ассемблерную функцию.

#[inline(always)]
pub fn usize_raw_load_acquire(dst: &mut usize, src: *const usize) {
    use std::arch::asm;

    assert!(src.is_aligned());
    debug_assert!(!src.is_null());

    #[cfg(target_arch = "x86_64")]
    unsafe {
        // In x86, things are properly ordered by default, and these operations
        // are atomic!
        asm! {
            "mov rax, [{src}]",
            "mov [{dst}], rax",
            src = in(reg) src,
            dst = in(reg) dst,
            out("rax") _,
            options(nostack, preserves_flags),
        }
    }

    #[cfg(not(target_arch = "x86_64"))]
    compile_error!("unsupported arch");
}

Что интересно (возможно), так это то, что я пометил его как безопасный...

Чтение указателей пугает по многим причинам, но я не понимаю, как любая из этих причин применима здесь! Однако давайте просто сосредоточимся на деталях происхождения и проигнорируем другие мои утверждения о том, что делает этот код.

По сути, я считаю, что src может быть указателем на что угодно... и его можно безопасно читать в любой среде (например, даже если &mut существует для того же объекта)

Почему я могу ошибаться?

Даже игнорируя указатели на недопустимые адреса, предположим, что у вас есть src_ref, который является &mut по тому же адресу, что и src, если вы это сделаете: *src_ref = 42; usize_raw_load_acquire (&mut dst, src); *src_ref = 0; значение в dst не определено. На практике вполне вероятно, что он будет 42 в отладочных сборках, а все, что было в src до этого кода, в релизных сборках. Но это тоже не гарантировано, теоретически может случиться что угодно.

Jmb 04.04.2024 19:51

Я изо всех сил пытаюсь понять, как чтение usize из NonNull::dangling().as_ptr() можно назвать безопасным.

kmdreko 04.04.2024 20:14
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
2
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

даже если &mut существует для того же объекта

Эта часть определенно неверна. Rust не определяет уникальность ссылок &mut только внутри Абстрактной машины; он определен и вне его — например, FFI тоже должен их соблюдать, и inline asm точно так же.

В частности, единственное, что определено в правилах псевдонимов , это то, что они следуют, по крайней мере, ноалиасам LLVM (хотя они и более строгие), а LLVM определяет это как:

Это указывает на то, что к ячейкам памяти, доступ к которым осуществляется через значения указателя на основе аргумента или возвращаемого значения, также не осуществляется доступ во время выполнения функции через значения указателя, не основанные на аргументе или возвращаемом значении.

Вы можете видеть, что это определяется с точки зрения доступа к памяти, а не чтения IR.

В практическом аспекте LLVM может оптимизировать другой код, исходя из предположения, что значение не считывается (например, перезаписать сохраненное значение), и это может привести к поломке другого кода и/или вашего кода.

См. также UCG#331 — Может ли встроенное чтение ассемблера читать uninit и нарушать noalias?.

Остальные инварианты, вероятно, в порядке. Если вы передадите неверный указатель, процессор перехватит его, но он не находится внутри абстрактной машины, поэтому Rust не делает никаких предположений по этому поводу. Это похоже на вызов (через FFI) на язык, на котором нет UB.

Другие вопросы по теме