UB in accept
The following code will give a page fault inside listener.accept()
when you connect through telnet from another machine:
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{self, Result, Write};
use std::net::{TcpListener, TcpStream};
use std::os::fd::AsRawFd;
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
use std::os::unix::process::CommandExt;
use std::process::{Child, Command, Stdio};
use std::sync::Mutex;
#[cfg(target_os = "redox")]
use redox_termios::Winsize;
use getpty::getpty;
mod getpty;
#[cfg(not(target_os = "redox"))]
pub fn pre_exec() -> Result<()> {
use libc;
unsafe {
libc::setsid();
libc::ioctl(0, libc::TIOCSCTTY, 1);
}
Ok(())
}
#[cfg(target_os = "redox")]
pub fn pre_exec() -> Result<()> {
Ok(())
}
fn handle(stream: TcpStream, master_fd: RawFd, process: Child) {
#[cfg(not(target_os = "redox"))]
unsafe {
let size = libc::winsize {
ws_row: 30,
ws_col: 80,
ws_xpixel: 0,
ws_ypixel: 0,
};
libc::ioctl(master_fd, libc::TIOCSWINSZ, &size as *const libc::winsize);
}
#[cfg(target_os = "redox")]
{
let winsize =
syscall::dup(master_fd as usize, b"winsize").expect("failed to get winsize property");
let size = Winsize {
ws_row: 30,
ws_col: 80,
};
let ret = syscall::write(winsize, &size);
syscall::close(winsize).expect("failed to close winsize property");
ret.expect("failed to set winsize property");
}
let master = unsafe { File::from_raw_fd(master_fd) };
let process = Mutex::new(process);
std::thread::scope(|scope| {
scope.spawn(|| {
if let Err(err) = std::io::copy(&mut &stream, &mut &master) {
eprintln!("error copying streams: {err}");
process
.lock()
.unwrap()
.kill()
.expect("failed to kill child process");
process
.lock()
.unwrap()
.wait()
.expect("failed to wait for child process");
std::process::exit(2);
}
});
if let Err(err) = std::io::copy(&mut &master, &mut &stream) {
eprintln!("error copying streams: {err}");
process
.lock()
.unwrap()
.kill()
.expect("failed to kill child process");
process
.lock()
.unwrap()
.wait()
.expect("failed to wait for child process");
std::process::exit(3);
}
});
process
.lock()
.unwrap()
.kill()
.expect("failed to kill child process");
process
.lock()
.unwrap()
.wait()
.expect("failed to wait for child process");
}
fn telnet() {
let listener = TcpListener::bind("0.0.0.0:23").unwrap();
loop {
eprintln!("listening");
// eprintln!("{}", unsafe {
// libc::accept(listener.as_raw_fd(), core::ptr::null_mut(), core::ptr::null_mut())
// });
let stream = match listener.accept() {
Ok((stream, _)) => stream,
Err(err) => {
eprintln!("accept error: {}", err);
continue;
}
};
eprintln!("accepted stream");
//if unsafe { libc::fork() as usize } == 0 {
//drop(listener);
eprintln!("creating pty");
let (master_fd, tty_path) = getpty();
eprintln!("created pty");
let slave_stdin = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
let slave_stdout = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
let slave_stderr = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
eprintln!("opened pty");
match unsafe {
Command::new("login")
.env("COLUMNS", "80")
.env("LINES", "30")
.env("TERM", "linux")
.env("TTY", tty_path)
.stdin(Stdio::from_raw_fd(slave_stdin.into_raw_fd()))
.stdout(Stdio::from_raw_fd(slave_stdout.into_raw_fd()))
.stderr(Stdio::from_raw_fd(slave_stderr.into_raw_fd()))
.pre_exec(|| pre_exec())
.spawn()
} {
Ok(process) => {
eprintln!("spawned login");
handle(stream, master_fd, process);
}
Err(err) => {
let _ = writeln!(io::stderr().lock(), "failed to execute 'login': {err}");
}
}
// std::process::exit(0);
//}
}
}
fn main() {
let mut background = false;
for arg in env::args().skip(1) {
match arg.as_ref() {
"-b" => background = true,
_ => (),
}
}
println!("Telnet");
if background {
if unsafe { libc::fork() as usize } == 0 {
telnet();
}
} else {
telnet();
panic!();
}
}