NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: pkg/57708: rust problem when building firefox



The attached Rust program prints the parameters for both the main
thread and non-main threads.  It should be relatively easy to adapt to
the code in Rust std/sys/thread.rs, I expect.

Plop it in src/main.rs next to a Cargo.toml file with the following
content, and run with `cargo run':

[package]
name = "stackguard"
version = "0.1.0"
edition = "2021"

[dependencies]
libc = "^0.2.150"
//
// __MACHINE_STACK_GROWS_UP (hppa):
//
//      +---------------+
//      |               |
//      | guard pages   | guardsize
//      |               |
//      +---------------+ guardaddr = stackbase + stacksize
//      |               |
//      | stack pages   | stacksize
//      |               |
//      +---------------+ stackaddr = stackbase
//
// !__MACHINE_STACK_GROWS_UP (i.e., stack grows down -- most
// architectures):
//
//      +---------------+ stackbase
//      |               |
//      | stack pages   | stacksize
//      |               |
//      +---------------+ stackaddr = stackbase - stacksize
//      |               |
//      | guard pages   | guardsize
//      |               |
//      +---------------+ guardaddr = stackbase - stacksize - guardsize
//

use libc;
use std::io;
use std::mem;
use std::os::raw::c_int;
use std::os::raw::c_uint;
use std::os::raw::c_ulong;
use std::os::raw::c_void;
use std::ptr;

const __MACHINE_STACK_GROWS_UP: bool = false; // XXX

#[repr(C)]
struct AuxInfo {
    a_type: u32,                // Elf32_Word, Elf64_Word
    a_v: c_ulong,               // Elf32_Word, Elf64_Xword
}

const AT_NULL: u32 = 0;
const AT_STACKBASE: u32 = 13;

extern { fn _dlauxinfo() -> *const AuxInfo; }

const VM_GUARD_SIZE: c_int = 17;

fn pth(error: c_int) -> io::Result<()> {
    if error == 0 {
        Ok(())
    } else {
        Err(io::Error::from_raw_os_error(error))
    }
}

fn getmainstack() -> io::Result<(*mut c_void, usize, *mut c_void, usize)> {
    // Get the stack guard size by reading the unsigned int
    // vm.guard_size sysctl.
    //
    // XXX In principle, this is racy because there is a window between
    // when the kernel reads the sysctl to determine the guard page
    // allocation and when we read the sysctl to find what that was,
    // during which time a privileged process could change the sysctl
    // without affecting the guard page allocation, but
    //
    // (a) it's unlikely that this will happen, and
    //
    // (b) NetBSD doesn't currently provide a non-racy way to do this,
    //     like maybe an AT_NETBSD_GUARDSIZE auxinfo entry.
    //
    let mut mib = [libc::CTL_VM, VM_GUARD_SIZE];
    let mut guardsize: c_uint = 0;
    let mut len: usize = mem::size_of::<c_uint>();
    if unsafe { libc::sysctl(
        mib.as_mut_ptr(),       // name
        mib.len() as c_uint,    // namelen
        &mut guardsize as *mut _ as *mut _,     // oldp
        &mut len,               // oldlenp
        ptr::null_mut(),        // newp
        0,                      // newlenp
    ) } == -1 {
        return Err(io::Error::last_os_error());
    }

    // Get the stack base by reading the AT_STACKBASE _dlauxinfo item.
    // Note: This is the initial stack pointer -- not the numerically
    // lowest address; it grows up or down depending on
    // __MACHINE_STACK_GROWS_UP.
    //
    let mut auxinfo = unsafe { _dlauxinfo() };
    let mut stackbase: *mut c_void = ptr::null_mut();
    while unsafe { (*auxinfo).a_type } != AT_NULL {
        let (a_type, a_v) = unsafe { ((*auxinfo).a_type, (*auxinfo).a_v) };
        println!("a_type={} a_v=0x{:x}", a_type, a_v);
        if a_type == AT_STACKBASE {
            stackbase = unsafe { mem::transmute((*auxinfo).a_v) };
            break
        }
        auxinfo = unsafe { auxinfo.offset(1) };
    }

    // Get the stack size by reading the hard stack rlimit, which the
    // kernel used to determine the maximum stack allocation (not
    // including the guard pages).
    //
    // XXX In principle, this is racy because there is a window between
    // when the kernel reads the rlimit to determine the maximum stack
    // allocation and when we read the rlimit to find what that was,
    // during which time a privileged process could change the rlimit
    // without affecting the stack allocation, but
    //
    // (a) it's unlikely that this will happen, and
    //
    // (b) NetBSD doesn't currently provide a non-racy way to do this,
    //     like maybe an AT_NETBSD_STACKSIZE auxinfo entry.
    //
    let mut rlim = unsafe { mem::zeroed() };
    if unsafe { libc::getrlimit(libc::RLIMIT_STACK, &mut rlim) } == -1 {
        return Err(io::Error::last_os_error());
    }
    let stacksize = rlim.rlim_max as usize;
    let istacksize = stacksize as isize;
    let iguardsize = guardsize as isize;

    // Determine the numerically least addresses of the stack and
    // guard.  See diagram at top about stacks that grow up vs stacks
    // that grow down.
    //
    let stackaddr = if __MACHINE_STACK_GROWS_UP {
        stackbase
    } else {
        unsafe { stackbase.offset(-istacksize) }
    };
    let guardaddr = if __MACHINE_STACK_GROWS_UP {
        unsafe { stackbase.offset(istacksize) }
    } else {
        unsafe { stackbase.offset(-istacksize - iguardsize) }
    };

    Ok((stackaddr, stacksize, guardaddr, guardsize as usize))
}

fn getthreadattrstack(attr: *mut libc::pthread_attr_t)
  -> io::Result<(*mut c_void, usize, *mut c_void, usize)> {
    let mut stackaddr = ptr::null_mut();
    let mut stacksize: usize = 0;
    let mut guardsize: usize = 0;
    pth(unsafe {
        libc::pthread_attr_getstack(attr, &mut stackaddr, &mut stacksize)
    })?;
    pth(unsafe { libc::pthread_attr_getguardsize(attr, &mut guardsize) })?;
    let guardaddr = if __MACHINE_STACK_GROWS_UP {
        unsafe { stackaddr.offset(stacksize as isize) }
    } else {
        unsafe { stackaddr.offset(-(guardsize as isize)) }
    };
    Ok((stackaddr, stacksize, guardaddr, guardsize))
}

fn getthreadstack() -> io::Result<(*mut c_void, usize, *mut c_void, usize)> {
    let t = unsafe { libc::pthread_self() };
    let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
    pth(unsafe { libc::pthread_getattr_np(t, &mut attr) })?;
    let r = getthreadattrstack(&mut attr);
    pth(unsafe { libc::pthread_attr_destroy(&mut attr) })
        .expect("pthread_attr_destroy should never fail");
    r
}

fn showstack(name: &str, stack: (*mut c_void, usize, *mut c_void, usize)) {
    let (stackaddr, stacksize, guardaddr, guardsize) = stack;
    println!("{} stack @ [{:p}, {:p})",
             name,
             stackaddr, unsafe { stackaddr.offset(stacksize as isize) });
    println!("{} guard @ [{:p}, {:p})",
             name,
             guardaddr, unsafe { guardaddr.offset(guardsize as isize) });
}

fn main() {
    match getmainstack() {
        Ok(stack) => showstack("main", stack),
        Err(e) => println!("getmainstack: {}", e),
    }
    match getthreadstack() {
        Ok(stack) => showstack("thread", stack),
        Err(e) => println!("getthreadstack: {}", e),
    }
}


Home | Main Index | Thread Index | Old Index