Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x86_64-unknown-linux-gnu using 'target-feature=+crt-static' exits with: Segmentation fault (core dumped). #100110

Open
noonuid opened this issue Aug 3, 2022 · 12 comments
Labels
A-linkage Area: linking into static, shared libraries and binaries A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. C-bug Category: This is a bug. O-linux Operating system: Linux

Comments

@noonuid
Copy link

noonuid commented Aug 3, 2022

I expected to see this happen: complied program with static linking runs normally.

Instead, this happened:

  • when i debug, it exits with: Stop reason: signal SIGSEGV: invalid address (fault address: 0xe5)
  • when i run the executable directly, it exits with: Segmentation fault (core dumped)

But when i debug the executable without static linking, it works well. And when i build with '--target x86_64-unknown-linux-musl', it works well, too.

I think it's not a code error, but some config problems.

Meta

rustc --version --verbose:

rustc 1.59.0 (9d1b2106e 2022-02-23)
binary: rustc
commit-hash: 9d1b2106e23b1abd32fce1f17267604a5102f57a
commit-date: 2022-02-23
host: x86_64-unknown-linux-gnu
release: 1.59.0
LLVM version: 13.0.0
Backtrace

image

@noonuid noonuid added the C-bug Category: This is a bug. label Aug 3, 2022
@saethlin
Copy link
Member

saethlin commented Aug 4, 2022

Can you provide or point to enough code that we can use to reproduce this?

@noonuid noonuid changed the title static linking with glibc is ok when compile, but Static linking with glibc is ok when compile, but the executable can't run, while the one which is not using 'target-feature=+crt-static' is totally fine. Aug 4, 2022
@noonuid
Copy link
Author

noonuid commented Aug 4, 2022

thread.rs

use crate::common::consts::{
    EVENT_SLOT_PTY_CMD, PTY_FLAG_INIT_BLOCK, PTY_REMOVE_INTERVAL, PTY_WS_MSG,
    WS_MSG_TYPE_PTY_ERROR, WS_MSG_TYPE_PTY_INPUT, WS_MSG_TYPE_PTY_OUTPUT, WS_MSG_TYPE_PTY_READY,
    WS_MSG_TYPE_PTY_RESIZE, WS_MSG_TYPE_PTY_START, WS_MSG_TYPE_PTY_STOP,
};
use crate::common::evbus::EventBus;
use crate::conpty::{PtySession, PtySystem};

use crate::types::ws_msg::{PtyError, PtyInput, PtyReady, PtyResize, PtyStop, WsMsg};
use crate::types::ws_msg::{PtyOutput, PtyStart};
use log::{error, info};
use serde::Serialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::{AtomicU64, Ordering, Ordering::SeqCst};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::io::AsyncReadExt;
use tokio::runtime::Runtime;
use tokio::time::timeout;

cfg_if::cfg_if! {
    if #[cfg(unix)] {
        use super::unix::install_scripts;
        use super::unix::ConPtySystem;
        use users::get_current_username;
    } else if #[cfg(windows)] {
        use super::windows::ConPtySystem;
        use crate::executor::powershell_command::get_current_user;
    }
}

#[derive(Clone)]
pub(crate) struct Session {
    session_id: String,
    pty_session: Arc<dyn PtySession + Send + Sync>,
    writer: Arc<Mutex<File>>,
    last_input_time: Arc<AtomicU64>,
    is_stoped: Arc<AtomicBool>,
}

#[derive(Clone)]
pub(crate) struct SessionManager {
    ws_seq_num: Arc<AtomicU64>,
    running_task_num: Arc<AtomicU64>,
    pub(crate) session_map: Arc<RwLock<HashMap<String, Arc<Session>>>>,
    pub(crate) event_bus: Arc<EventBus>,
    runtime: Arc<Runtime>,
}

pub fn run(dispatcher: &Arc<EventBus>, running_task_num: &Arc<AtomicU64>) {
    #[cfg(unix)]
    install_scripts();

    let context = Arc::new(SessionManager {
        event_bus: dispatcher.clone(),
        session_map: Arc::new(RwLock::new(HashMap::default())),
        running_task_num: running_task_num.clone(),
        ws_seq_num: Arc::new(AtomicU64::new(0)),
        runtime: Arc::new(Runtime::new().unwrap()),
    });
    register_pty_hander(context.clone());
}

fn register_pty_hander(sm: Arc<SessionManager>) {
    let self_0 = sm.clone();
    let self_1 = sm.clone();
    let self_2 = sm.clone();
    let self_3 = sm.clone();
    sm.event_bus
        .slot_register(
            EVENT_SLOT_PTY_CMD,
            WS_MSG_TYPE_PTY_START,
            move |value: String| {
                self_0.handle_pty_start(value);
            },
        )
        .slot_register(
            EVENT_SLOT_PTY_CMD,
            WS_MSG_TYPE_PTY_STOP,
            move |value: String| {
                self_1.handle_pty_stop(&value);
            },
        )
        .slot_register(
            EVENT_SLOT_PTY_CMD,
            WS_MSG_TYPE_PTY_RESIZE,
            move |value: String| {
                self_2.handle_pty_resize(value);
            },
        )
        .slot_register(
            EVENT_SLOT_PTY_CMD,
            WS_MSG_TYPE_PTY_INPUT,
            move |value: String| {
                self_3.handle_pty_input(value);
            },
        );
}

impl SessionManager {
    fn handle_pty_start(&self, value: String) {
        info!("=>handle_pty_start {}", value);
        let pty_start: PtyStart = match serde_json::from_str(&value) {
            Ok(v) => v,
            _ => return,
        };

        let session_id = pty_start.session_id;
        let user_name = if pty_start.user_name.len() == 0 {
            #[cfg(unix)]
            {
                let name = get_current_username().unwrap();
                String::from(name.to_str().unwrap())
            }
            #[cfg(windows)]
            get_current_user()
        } else {
            pty_start.user_name
        };

        let mut flag: u32 = 0;
        if pty_start.init_block {
            flag = flag | PTY_FLAG_INIT_BLOCK
        }

        self.running_task_num.fetch_add(1, SeqCst);
        let pty_session =
            match ConPtySystem::default().openpty(&user_name, pty_start.cols, pty_start.rows, flag)
            {
                Ok(session) => session,
                Err(e) => {
                    error!("=>openpty err {}", e.to_string());
                    let msg = self.build_error_msg(session_id.clone(), e);
                    self.event_bus.dispatch(PTY_WS_MSG, msg);
                    self.running_task_num.fetch_sub(1, SeqCst);
                    return;
                }
            };

        let input_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let writer = pty_session.get_writer().unwrap();
        let session = Arc::new(Session {
            session_id: session_id.clone(),
            pty_session,
            writer: Arc::new(Mutex::new(writer)),
            last_input_time: Arc::new(AtomicU64::new(input_time)),
            is_stoped: Arc::new(AtomicBool::new(false)),
        });

        self.session_map
            .write()
            .unwrap()
            .insert(session_id.clone(), session.clone());

        let msg = self.build_ready_msg(session_id.clone());
        self.event_bus.dispatch(PTY_WS_MSG, msg);

        let self_0 = self.clone();
        self.runtime
            .spawn(async move { self_0.report_output(session).await });

        info!("handle_pty_start success");
    }

    fn handle_pty_stop(&self, value: &str) {
        info!("handle_pty_stop {}", value);
        let pty_stop: PtyStop = match serde_json::from_str(&value) {
            Ok(v) => v,
            _ => return,
        };
        self.remove_session(&pty_stop.session_id);
        info!("handle_pty_stop session {} removed ", &pty_stop.session_id);
    }

    fn handle_pty_resize(&self, value: String) {
        info!("handle_pty_resize {}", value);
        let pty_resize: PtyResize = match serde_json::from_str(&value) {
            Ok(v) => v,
            _ => return,
        };

        let session_id = pty_resize.session_id.clone();
        self.work_in_session(&session_id, move |session| {
            let _ = session.pty_session.resize(pty_resize.cols, pty_resize.rows);
        });
    }

    fn handle_pty_input(&self, value: String) {
        let pty_input: PtyInput = match serde_json::from_str(&value) {
            Ok(v) => v,
            _ => return,
        };

        let input_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        if let Ok(input) = base64::decode(pty_input.input) {
            let session_id = pty_input.session_id.clone();
            self.work_in_session(&session_id, move |session| {
                let _ = session.writer.lock().unwrap().write(&input).unwrap();
                session.last_input_time.store(input_time, SeqCst);
            })
        }
    }

    async fn report_output(&self, session: Arc<Session>) {
        info!("=>report_output {}", session.session_id);
        let duration = Duration::from_millis(100);
        let mut reader = tokio::fs::File::from_std(session.pty_session.get_reader().unwrap());
        loop {
            if session.is_stoped.load(SeqCst) {
                info!(
                    "report_output find {} session stoped,break",
                    session.session_id
                );
                break;
            }

            //no input about five miniutes, break
            if !self.check_last_input_time(&session) {
                info!(
                    "pty session {}  check_last_input_time fail",
                    session.session_id
                );
                break;
            }

            let mut buffer: [u8; 1024] = [0; 1024];
            let timeout_read = timeout(duration, reader.read(&mut buffer[..])).await;
            if timeout_read.is_err() {
                continue;
            }
            match timeout_read.unwrap() {
                Ok(size) => {
                    if size > 0 {
                        let msg =
                            self.build_output_msg(session.session_id.clone(), &mut buffer[0..size]);
                        self.event_bus.dispatch(PTY_WS_MSG, msg);
                    } else {
                        info!("pty session {} read size is 0 close", session.session_id);
                        break;
                    }
                }
                Err(e) => {
                    info!(
                        "pty session {} report_output err, {}",
                        session.session_id, e
                    );

                    let msg = self.build_error_msg(
                        session.session_id.clone(),
                        format!("Session {} terminated", session.session_id),
                    );
                    self.event_bus.dispatch(PTY_WS_MSG, msg);
                    break;
                }
            }
        }
        self.remove_session(&session.session_id);
        info!("report_output {} finished", session.session_id);
    }

    fn remove_session(&self, session_id: &str) {
        if let Some(session) = self.session_map.write().unwrap().remove(session_id) {
            info!("remove_session  {} removed", session_id);
            session.is_stoped.store(true, SeqCst);
            self.running_task_num.fetch_sub(1, Ordering::SeqCst);
        }
    }

    fn check_last_input_time(&self, session: &Arc<Session>) -> bool {
        let last = session.last_input_time.load(SeqCst);
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        return if now - last > PTY_REMOVE_INTERVAL {
            false //time out
        } else {
            true
        };
    }

    pub(crate) fn work_in_session<F>(&self, session_id: &str, func: F)
    where
        F: Fn(Arc<Session>) + 'static + Sync + Send,
    {
        if let Some(session) = self.session_map.read().unwrap().get(session_id) {
            func(session.clone());
        } else {
            error!("Session {} not find", session_id);
            let msg = self.build_error_msg(
                session_id.to_string(),
                format!("Session {} not exist", session_id),
            );
            self.event_bus.dispatch(PTY_WS_MSG, msg);
        }
    }

    fn build_output_msg(&self, session_id: String, buf: &mut [u8]) -> String {
        let data = base64::encode(buf);
        let pty_output = PtyOutput {
            session_id,
            output: data,
        };
        self.build_msg(WS_MSG_TYPE_PTY_OUTPUT, pty_output)
    }

    fn build_ready_msg(&self, session_id: String) -> String {
        let pty_ready = PtyReady { session_id };
        self.build_msg(WS_MSG_TYPE_PTY_READY, pty_ready)
    }

    fn build_error_msg(&self, session_id: String, reason: String) -> String {
        let pty_ready = PtyError { session_id, reason };
        self.build_msg(WS_MSG_TYPE_PTY_ERROR, pty_ready)
    }

    fn build_msg<T>(&self, msg_type: &str, msg_body: T) -> String
    where
        T: Serialize,
    {
        let value = serde_json::to_value(msg_body).unwrap();
        let msg = WsMsg {
            r#type: msg_type.to_string(),
            seq: self.ws_seq_num.fetch_add(1, SeqCst),
            data: Some(value),
        };
        serde_json::to_string(&msg).unwrap()
    }
}

#[cfg(test)]
mod test {
    #[cfg(unix)]
    use crate::conpty::unix::ConPtySystem;
    #[cfg(windows)]
    use crate::conpty::windows::ConPtySystem;
    use crate::conpty::PtySystem;
    #[cfg(windows)]
    use crate::executor::powershell_command::get_current_user;
    use log::info;
    use std::time::Duration;
    use tokio::io::{AsyncReadExt, AsyncWriteExt};
    use tokio::time::timeout;
    #[cfg(unix)]
    use users::get_current_username;

    #[tokio::test]
    async fn test() {
        #[cfg(unix)]
        let user_name = String::from(get_current_username().unwrap().to_str().unwrap());
        #[cfg(windows)]
        let user_name = get_current_user();

        let session = ConPtySystem::default()
            .openpty(&user_name, 200, 100, 0)
            .unwrap();

        let mut reader = tokio::fs::File::from_std(session.get_reader().unwrap());
        let mut writer = tokio::fs::File::from_std(session.get_writer().unwrap());

        //consume data
        loop {
            let mut temp: [u8; 1024] = [0; 1024];
            let duration = Duration::from_secs(10);
            let timeout_read = timeout(duration, reader.read(&mut temp[..])).await;
            if timeout_read.is_err() {
                break;
            }
        }

        writer.write("echo \"test\" \r".as_bytes()).await.unwrap();
        let mut buffer: [u8; 1024] = [0; 1024];
        let mut len = 0;

        //read pwd output
        loop {
            let duration = Duration::from_secs(4);
            let timeout_read = timeout(duration, reader.read(&mut buffer[len..])).await;
            if timeout_read.is_err() {
                break;
            }
            len += timeout_read.unwrap().unwrap()
        }

        let output = String::from_utf8_lossy(&buffer[..len]);
        info!("output is {}", output);
        let result = output.to_string().contains("test");
        std::mem::drop(session);
        assert!(result);
    }
}

@noonuid
Copy link
Author

noonuid commented Aug 4, 2022

unix.rs

use super::{PtySession, PtySystem};
use crate::common::consts::{
    self, BASH_PREEXC, BLOCK_INIT, COREOS_BASH_PREEXC, COREOS_BLOCK_INIT, PTY_FLAG_INIT_BLOCK,
};
use crate::conpty::utmpx::{LoginContext, DEFAULT_DEVICE, FD_PATH};
use crate::executor::proc::BaseCommand;
use crate::executor::shell_command::{build_envs, cmd_path};
use libc::{self, waitpid, winsize};
use log::{error, info};
use std::fs::read_link;
use std::fs::{create_dir_all, read_to_string, set_permissions, write, File, Permissions};
use std::io::Write;
use std::os::unix::prelude::{AsRawFd, CommandExt, FromRawFd, PermissionsExt, RawFd};
use std::path::Path;
use std::path::PathBuf;
use std::process::{Child, Command};
use std::ptr::null_mut;
use std::sync::{Arc, Mutex};
use std::{io, ptr};
use users::get_user_by_name;
use users::os::unix::UserExt;
use users::User;

struct Inner {
    master: File,
    slave: File,
    child: Child,
}

fn openpty(user: User, cols: u16, rows: u16) -> Result<(File, File), String> {
    let mut master: RawFd = -1;
    let mut slave: RawFd = -1;

    let mut size = winsize {
        ws_col: cols,
        ws_row: rows,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };

    unsafe {
        if 0 != libc::openpty(
            &mut master,
            &mut slave,
            ptr::null_mut(),
            ptr::null_mut(),
            &mut size,
        ) {
            return Err(format!("openpty fail {}", io::Error::last_os_error()));
        };

        libc::fchown(slave, user.uid(), user.primary_group_id());
    }

    let master = unsafe { File::from_raw_fd(master) };
    let slave = unsafe { File::from_raw_fd(slave) };
    Ok((master, slave))
}

#[derive(Default)]
pub struct ConPtySystem {}

impl PtySystem for ConPtySystem {
    fn openpty(
        &self,
        user_name: &str,
        cols: u16,
        rows: u16,
        #[allow(dead_code)] flag: u32,
    ) -> Result<std::sync::Arc<dyn PtySession + Send + Sync>, String> {
        let user = get_user_by_name(user_name).ok_or(format!("user {} not exist", user_name))?;
        let shell_path = cmd_path("bash").ok_or("bash not exist".to_string())?;
        let home_path = user.home_dir().to_str().unwrap_or_else(|| "/tmp");

        let envs = build_envs(user_name, home_path, &shell_path);
        let (mut master, slave) = openpty(user.clone(), cols, rows)?;

        let mut cmd = Command::new("bash");
        unsafe {
            cmd.pre_exec(move || {
                for signo in &[
                    libc::SIGCHLD,
                    libc::SIGINT,
                    libc::SIGQUIT,
                    libc::SIGTERM,
                    libc::SIGALRM,
                ] {
                    libc::signal(*signo, libc::SIG_DFL);
                }

                libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGHUP);

                if libc::setsid() == -1 {
                    return Err(io::Error::last_os_error());
                }
                Ok(())
            });
        }
        let child = cmd
            .args(["--login"])
            .uid(0)
            .gid(0)
            .stdin(slave.try_clone().unwrap())
            .stdout(slave.try_clone().unwrap())
            .stderr(slave.try_clone().unwrap())
            .envs(envs)
            .spawn()
            .map_err(|e| format!("spwan err {}", e))?;

        if (flag & PTY_FLAG_INIT_BLOCK) != 0 {
            let init_block = format!("source {};clear\n", BLOCK_INIT);
            let _ = master.write(init_block.as_bytes());
        }

        let pid = child.id() as i32;
        let path_buf = PathBuf::from(format!("{}{}", FD_PATH, slave.as_raw_fd()));
        let path = read_link(path_buf).unwrap_or(PathBuf::from(DEFAULT_DEVICE));
        let path = path.to_str().unwrap_or(DEFAULT_DEVICE);

        let lc = LoginContext::new(pid, path, &user_name, "127.0.0.1");
        lc.login();

        return Ok(Arc::new(UnixPtySession {
            inner: Arc::new(Mutex::new(Inner {
                master,
                slave,
                child,
            })),
        }));
    }
}

pub struct UnixPtySession {
    inner: Arc<Mutex<Inner>>,
}

impl PtySession for UnixPtySession {
    fn resize(&self, cols: u16, rows: u16) -> Result<(), String> {
        let size = winsize {
            ws_row: rows,
            ws_col: cols,
            ws_xpixel: 0,
            ws_ypixel: 0,
        };

        let result = unsafe {
            let inner = self.inner.lock().unwrap();
            libc::ioctl(
                inner.master.as_raw_fd(),
                libc::TIOCSWINSZ,
                &size as *const _,
            )
        };
        if result != 0 {
            Err(format!("{}", io::Error::last_os_error()))
        } else {
            Ok(())
        }
    }

    fn get_reader(&self) -> Result<std::fs::File, std::string::String> {
        self.get_writer()
    }

    fn get_writer(&self) -> Result<std::fs::File, std::string::String> {
        let inner = self.inner.lock().unwrap();
        inner.master.try_clone().map_err(|e| format!("err {}", e))
    }
}

impl Drop for UnixPtySession {
    fn drop(&mut self) {
        let pid = self.inner.lock().unwrap().child.id() as i32;

        let fd = self.inner.lock().unwrap().slave.as_raw_fd();
        let path_buf = PathBuf::from(format!("{}{}", FD_PATH, fd));
        let path = read_link(path_buf).unwrap_or(PathBuf::from(DEFAULT_DEVICE));
        let path = path.to_str().unwrap_or(DEFAULT_DEVICE);
        let lc = LoginContext::new(pid, path, "", "127.0.0.1");
        lc.logout();

        BaseCommand::kill_process_group(pid as u32);
        unsafe {
            waitpid(pid, null_mut(), 0);
        }
    }
}

fn install_script(mem_data: String, path: &str) -> io::Result<()> {
    let file_data = read_to_string(path).unwrap_or_else(|_| "".to_string());
    if mem_data != file_data {
        //write
        let parent = Path::new(path).parent().unwrap();
        create_dir_all(parent)?;
        set_permissions(
            parent,
            Permissions::from_mode(consts::FILE_EXECUTE_PERMISSION_MODE),
        )?;
        write(path, mem_data)?;
        set_permissions(
            path,
            Permissions::from_mode(consts::FILE_EXECUTE_PERMISSION_MODE),
        )?;
    }
    Ok(())
}

pub(crate) fn install_scripts() {
    let is_coreos = match read_to_string("/etc/os-release") {
        Ok(data) => data.contains("CoreOS"),
        Err(_) => false,
    };
    info!("install_scripts is_coreos {}", is_coreos);
    let (pexec_path, init_path) = if is_coreos {
        (COREOS_BASH_PREEXC, COREOS_BLOCK_INIT)
    } else {
        (BASH_PREEXC, BLOCK_INIT)
    };

    let mem_data = String::from_utf8_lossy(include_bytes!("../../misc/bash-preexec.sh"));
    let _ = install_script(mem_data.to_string(), pexec_path).map_err(|e| {
        error!("install_scripts {} fail {}", pexec_path, e);
    });
    let mem_data = String::from_utf8_lossy(include_bytes!("../../misc/bash-init.sh"));
    let _ = install_script(mem_data.to_string(), init_path).map_err(|e| {
        error!("install_scripts {} fail {}", init_path, e);
    });
}

@noonuid
Copy link
Author

noonuid commented Aug 4, 2022

In thread.rs, program crashes in handle_pty_start function, but not the same line in every time, I marked it below.

    fn handle_pty_start(&self, value: String) {
        info!("=>handle_pty_start {}", value);
        let pty_start: PtyStart = match serde_json::from_str(&value) {
            Ok(v) => v,
            _ => return,
        };

        let session_id = pty_start.session_id;
        let user_name = if pty_start.user_name.len() == 0 {
            #[cfg(unix)]
            {
                let name = get_current_username().unwrap();<-------------------sometimes here.
                String::from(name.to_str().unwrap())
            }
            #[cfg(windows)]
            get_current_user()
        } else {
            pty_start.user_name
        };

        let mut flag: u32 = 0;
        if pty_start.init_block {
            flag = flag | PTY_FLAG_INIT_BLOCK
        }

        self.running_task_num.fetch_add(1, SeqCst);
        let pty_session =
            match ConPtySystem::default().openpty(&user_name, pty_start.cols, pty_start.rows, flag)
            {
                Ok(session) => session,
                Err(e) => {
                    error!("=>openpty err {}", e.to_string());
                    let msg = self.build_error_msg(session_id.clone(), e);
                    self.event_bus.dispatch(PTY_WS_MSG, msg);
                    self.running_task_num.fetch_sub(1, SeqCst);
                    return;
                }
            };

        let input_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let writer = pty_session.get_writer().unwrap();
        let session = Arc::new(Session {
            session_id: session_id.clone(),
            pty_session,
            writer: Arc::new(Mutex::new(writer)),
            last_input_time: Arc::new(AtomicU64::new(input_time)),
            is_stoped: Arc::new(AtomicBool::new(false)),
        });

        self.session_map
            .write()
            .unwrap()
            .insert(session_id.clone(), session.clone());

        let msg = self.build_ready_msg(session_id.clone());
        self.event_bus.dispatch(PTY_WS_MSG, msg);

        let self_0 = self.clone();
        self.runtime
            .spawn(async move { self_0.report_output(session).await });

        info!("handle_pty_start success");<-------------------sometimes here.
    }

@noonuid
Copy link
Author

noonuid commented Aug 4, 2022

In unix.rs, it crashes in openpty function.

    fn openpty(
        &self,
        user_name: &str,
        cols: u16,
        rows: u16,
        #[allow(dead_code)] flag: u32,
    ) -> Result<std::sync::Arc<dyn PtySession + Send + Sync>, String> {
        let user = get_user_by_name(user_name).ok_or(format!("user {} not exist", user_name))?;<-------------------sometimes here.
        let shell_path = cmd_path("bash").ok_or("bash not exist".to_string())?;
        let home_path = user.home_dir().to_str().unwrap_or_else(|| "/tmp");

        let envs = build_envs(user_name, home_path, &shell_path);
        let (mut master, slave) = openpty(user.clone(), cols, rows)?;

        let mut cmd = Command::new("bash");
        unsafe {
            cmd.pre_exec(move || {
                for signo in &[
                    libc::SIGCHLD,
                    libc::SIGINT,
                    libc::SIGQUIT,
                    libc::SIGTERM,
                    libc::SIGALRM,
                ] {
                    libc::signal(*signo, libc::SIG_DFL);
                }

                libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGHUP);

                if libc::setsid() == -1 {
                    return Err(io::Error::last_os_error());
                }
                Ok(())
            });
        }
        let child = cmd
            .args(["--login"])
            .uid(0)
            .gid(0)
            .stdin(slave.try_clone().unwrap())
            .stdout(slave.try_clone().unwrap())
            .stderr(slave.try_clone().unwrap())
            .envs(envs)
            .spawn()
            .map_err(|e| format!("spwan err {}", e))?;

        if (flag & PTY_FLAG_INIT_BLOCK) != 0 {
            let init_block = format!("source {};clear\n", BLOCK_INIT);
            let _ = master.write(init_block.as_bytes());
        }

        let pid = child.id() as i32;
        let path_buf = PathBuf::from(format!("{}{}", FD_PATH, slave.as_raw_fd()));
        let path = read_link(path_buf).unwrap_or(PathBuf::from(DEFAULT_DEVICE));
        let path = path.to_str().unwrap_or(DEFAULT_DEVICE);

        let lc = LoginContext::new(pid, path, &user_name, "127.0.0.1");
        lc.login();

        return Ok(Arc::new(UnixPtySession {
            inner: Arc::new(Mutex::new(Inner {
                master,
                slave,
                child,
            })),
        }));
    }
}

@workingjubilee
Copy link
Contributor

Mmm. Pushing a repo might have been more to-the-point.
What is the exact build command you used?

@noonuid
Copy link
Author

noonuid commented Aug 7, 2022

@workingjubilee

RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-unknown-linux-gnu

Following builds work as expected,
cargo build --target x86_64-unknown-linux-gnu
or
RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-unknown-linux-musl

I want to use glibc static linking.

@noonuid
Copy link
Author

noonuid commented Aug 8, 2022

This is the repo: https://github.com/Tencent/tat-agent
But the code is not the newest, so it does not have the code that I mentioned before.

Is there a precedent where executable builds with cargo build --target x86_64-unknown-linux-gnu works, but with RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-unknown-linux-gnu doesn't?

@workingjubilee @saethlin

@saethlin
Copy link
Member

saethlin commented Aug 8, 2022

The files you pasted into the issue thread are ambiguous, there are multiple files in this repo named thread.rs for example. If you can link to your own fork of this perhaps which has the modifications you want maybe I could reproduce the issue you are seeing.

The repo in question doesn't link on my machine with target-feature=+crt-static, I see the usual glibc warnings and then:

          /usr/bin/ld: /tmp/tat-agent/target/x86_64-unknown-linux-gnu/release/deps/libtokio-860b44f61df069aa.rlib(tokio-860b44f61df069aa.tokio.c88c0eba-cgu.6.rcgu.o): undefined reference to symbol '__tls_get_addr@@GLIBC_2.3'
          /usr/bin/ld: /usr/lib/ld-linux-x86-64.so.2: error adding symbols: DSO missing from command line
          collect2: error: ld returned 1 exit status

@noonuid
Copy link
Author

noonuid commented Aug 9, 2022

Ok, I have forked the repo into my own account. The problem branch is the w-who, and the key source code in the folder src/conpty.

Command to run the executable: target/x86_64-unknown-linux-gnu/debug/tat_agent -c -n -p. -p indicates the agent to work as pty server which makes developer can connect to it using the client code which locates in tests/pty.html.

I think you still cannot run the executable as it needs to run in a particular environment. But you can give me more help according to the full code. Thank you for your prompt reply.

@saethlin

@noonuid noonuid changed the title Static linking with glibc is ok when compile, but the executable can't run, while the one which is not using 'target-feature=+crt-static' is totally fine. x86_64-unknown-linux-gnu using 'target-feature=+crt-static' exits with: Segmentation fault (core dumped). Aug 10, 2022
@Urgau
Copy link
Contributor

Urgau commented Aug 19, 2022

#100711 seems that it might also to be related (also does some networking stuff) and have a much simpler MCVE:

use std::net::ToSocketAddrs;

pub fn main() {
    println!("before");
    let _ = "localhost:8080".to_socket_addrs(); // will segfault
    std::net::TcpStream::connect("localhost:8080").unwrap(); // will also segfault
    println!("hello world");
}

with rustc -C target-feature=+crt-static main.rs

@frederikhors
Copy link

Same here building in Dockerfile rust:1.67.0 image with:

RUN RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu --package app

and then copying the file in an alpine:3 docker image:

Segmentation fault

@workingjubilee workingjubilee added A-linkage Area: linking into static, shared libraries and binaries A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. O-linux Operating system: Linux labels Mar 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. C-bug Category: This is a bug. O-linux Operating system: Linux
Projects
None yet
Development

No branches or pull requests

5 participants