mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-04 20:54:13 +00:00
rustuv: Fix a use-after-free in TTY failure
If a TTY fails to get initialized, it still needs to have uv_close invoked on it. This fixes the problem by constructing the TtyWatcher struct before the call to uv_tty_init. The struct has a destructor on it which will close the handle properly. Closes #12666
This commit is contained in:
parent
cad7d24a23
commit
65cca4bd3f
@ -8,8 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::libc;
|
||||
use std::io::IoError;
|
||||
use std::libc;
|
||||
use std::ptr;
|
||||
use std::rt::rtio::RtioTTY;
|
||||
|
||||
use homing::{HomingIO, HomeHandle};
|
||||
@ -54,20 +55,24 @@ impl TtyWatcher {
|
||||
// If this file descriptor is indeed guessed to be a tty, then go ahead
|
||||
// with attempting to open it as a tty.
|
||||
let handle = UvHandle::alloc(None::<TtyWatcher>, uvll::UV_TTY);
|
||||
let mut watcher = TtyWatcher {
|
||||
tty: handle,
|
||||
stream: StreamWatcher::new(handle),
|
||||
home: io.make_handle(),
|
||||
fd: fd,
|
||||
};
|
||||
match unsafe {
|
||||
uvll::uv_tty_init(io.uv_loop(), handle, fd as libc::c_int,
|
||||
readable as libc::c_int)
|
||||
} {
|
||||
0 => {
|
||||
Ok(TtyWatcher {
|
||||
tty: handle,
|
||||
stream: StreamWatcher::new(handle),
|
||||
home: io.make_handle(),
|
||||
fd: fd,
|
||||
})
|
||||
}
|
||||
0 => Ok(watcher),
|
||||
n => {
|
||||
unsafe { uvll::free_handle(handle) }
|
||||
// On windows, libuv returns errors before initializing the
|
||||
// handle, so our only cleanup is to free the handle itself
|
||||
if cfg!(windows) {
|
||||
unsafe { uvll::free_handle(handle); }
|
||||
watcher.tty = ptr::null();
|
||||
}
|
||||
Err(UvError(n))
|
||||
}
|
||||
}
|
||||
@ -124,7 +129,9 @@ impl HomingIO for TtyWatcher {
|
||||
|
||||
impl Drop for TtyWatcher {
|
||||
fn drop(&mut self) {
|
||||
let _m = self.fire_homing_missile();
|
||||
self.close_async_();
|
||||
if !self.tty.is_null() {
|
||||
let _m = self.fire_homing_missile();
|
||||
self.close_async_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
74
src/test/run-pass/tcp-stress.rs
Normal file
74
src/test/run-pass/tcp-stress.rs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-linux see joyent/libuv#1189
|
||||
// ignore-fast
|
||||
// ignore-android needs extra network permissions
|
||||
// exec-env:RUST_LOG=debug
|
||||
|
||||
use std::libc;
|
||||
use std::io::net::ip::{Ipv4Addr, SocketAddr};
|
||||
use std::io::net::tcp::{TcpListener, TcpStream};
|
||||
use std::io::{Acceptor, Listener};
|
||||
|
||||
fn main() {
|
||||
// This test has a chance to time out, try to not let it time out
|
||||
spawn(proc() {
|
||||
use std::io::timer;
|
||||
timer::sleep(30 * 1000);
|
||||
println!("timed out!");
|
||||
unsafe { libc::exit(1) }
|
||||
});
|
||||
|
||||
let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 0 };
|
||||
let (p, c) = Chan::new();
|
||||
spawn(proc() {
|
||||
let mut listener = TcpListener::bind(addr).unwrap();
|
||||
c.send(listener.socket_name().unwrap());
|
||||
let mut acceptor = listener.listen();
|
||||
loop {
|
||||
let mut stream = match acceptor.accept() {
|
||||
Ok(stream) => stream,
|
||||
Err(error) => {
|
||||
debug!("accept failed: {:?}", error);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
stream.read_byte();
|
||||
stream.write([2]);
|
||||
}
|
||||
});
|
||||
let addr = p.recv();
|
||||
|
||||
let (p, c) = Chan::new();
|
||||
for _ in range(0, 1000) {
|
||||
let c = c.clone();
|
||||
spawn(proc() {
|
||||
match TcpStream::connect(addr) {
|
||||
Ok(stream) => {
|
||||
let mut stream = stream;
|
||||
stream.write([1]);
|
||||
let mut buf = [0];
|
||||
stream.read(buf);
|
||||
},
|
||||
Err(e) => debug!("{:?}", e)
|
||||
}
|
||||
c.send(());
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for all clients to exit, but don't wait for the server to exit. The
|
||||
// server just runs infinitely.
|
||||
drop(c);
|
||||
for _ in range(0, 1000) {
|
||||
p.recv();
|
||||
}
|
||||
unsafe { libc::exit(0) }
|
||||
}
|
Loading…
Reference in New Issue
Block a user