Skip to content

ICE for wcslen on Windows #3692

Closed
Closed
@cgettys-microsoft

Description

@cgettys-microsoft

Good day folks,
I hit a ICE today. I installed nightly today (and it's reporting 1.81) so I think it's up to date.

This might be related to #2904, but it's not a no_std crate. I see a shim for wcslen in https://github.com/rust-lang/miri/blob/dc286ac1432932b2a710789de40ae8bc4e7ea7c1/src/shims/foreign_items.rs - but it looks like that shim doesn't work for msvc windows targets at a minimum.
But I'm not an expert on Miri.
windows_core::PCWSTR and windows_core::PWSTR both trivially trigger in via their .len() method - meaning a lot of windows code could benefit.
Edit: repro has been minimized further, see #3692 (comment)

// windows-core 0.57.0 is the original source of the struct involved
// I've minimized the example (Trimming unnecessary derive macros, unused functions, et cetera)
// As best I can.
// https://github.com/microsoft/windows-rs/commit/15947886be041bebd0fe670fd4e73f18d95100d0

/// A pointer to a constant null-terminated string of 16-bit Unicode characters.
#[repr(transparent)]
pub struct PCWSTR(pub *const u16);

impl PCWSTR {
    /// Construct a new `PCWSTR` from a raw pointer
    pub const fn from_raw(ptr: *const u16) -> Self {
        Self(ptr)
    }

    /// String length without the trailing 0
    ///
    /// # Safety
    ///
    /// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
    pub unsafe fn len(&self) -> usize {
        //#[cfg(windows)]
        // It's specifically the inclusion of this that makes or breaks the repro
        // The other non-windows one does not trigger the ICE.
        let len = {
            extern "C" {
                fn wcslen(s: *const u16) -> usize;
            }
            wcslen(self.0)
        };

        // #[cfg(not(windows))]
        // let len = {
        //     let mut len = 0;
        //     let mut ptr = self.0;
        //     while ptr.read() != 0 {
        //         len += 1;
        //         ptr = ptr.add(1);
        //     }
        //     len
        // };

        len
    }
}

pub fn does_not_trigger_ice_without_proc_macro()
{
    // This isn't the most portable from endianness perspective. But it'll do.
    let my_str: [u16; 2] = ['a' as u16 , 0];
    let my_pcwstr = PCWSTR::from_raw(my_str.as_ptr());
    // SAFETY: doesn't matter if it's safe, point is that this doesn't trigger an ICE.
    let _my_ice_trigger = unsafe { my_pcwstr.len() };
}

#[cfg(test)]
mod tests
{
    use crate::{does_not_trigger_ice_without_proc_macro, PCWSTR};
    // /// This works as well to trigger it.
    // #[test]
    // pub fn cause_ice2()
    // {
    //     does_not_trigger_ice_without_proc_macro()
    // }

    #[test]
    pub fn cause_ice()
    {
        // This isn't the most portable from endianness perspective. But it'll do.
        let my_str: [u16; 2] = ['a' as u16 , 0];
        let my_pcwstr = PCWSTR::from_raw(my_str.as_ptr());
        // SAFETY: doesn't matter if it's safe, point is that this doesn't trigger an ICE.
        let _my_ice_trigger = unsafe { my_pcwstr.len() };
    }
}

It also triggers on PWSTR. I don't think it repros on strlen though - which makes sense with the backtrace.
wcslen is utf16, not utf32, on Windows.

rustc-ice-2024-06-21T01_12_48-101996.txt

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