diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index f3677d0875..98b152eaa6 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -311,41 +311,43 @@ pub fn dup2( buf: UserSliceRo, token: &mut CleanLockToken, ) -> Result { - // A self-dup2 (fd == new_fd) with an EMPTY buffer is a no-op. But a - // self-dup2 WITH a buffer is meaningful — e.g. relibc issues dup2(ft, ft, - // "refresh") to re-materialize the inherited filetable — so it must fall - // through to duplicate_file() rather than short-circuiting. - if fd == new_fd && buf.is_empty() { - return Ok(new_fd); - } + if fd == new_fd { + // Self-dup: a no-op if no buffer is supplied. But a self-dup2 WITH a + // buffer is a meaningful in-place operation — e.g. relibc issues + // dup2(ft, ft, "refresh") to re-materialize the inherited filetable, so + // it must NOT short-circuit. Duplicate first (fd is still valid), then + // replace the descriptor in place. Only this self-dup path is new; the + // normal path below is unchanged so ordinary dup2 (process-spawn stdio) + // keeps its exact original behavior. + if buf.is_empty() { + return Ok(new_fd); + } + let new_file = duplicate_file(fd, buf, false, token)?; + let old_file = { + let current_lock = context::current(); + let mut current = current_lock.read(token.token()); + let (context, mut token) = current.token_split(); + let old_file = context.remove_file(new_fd, &mut token); + context + .insert_file(new_fd, new_file, &mut token) + .ok_or(Error::new(EMFILE))?; + old_file + }; + if let Some(old) = old_file { + let _ = old.close(token); + } + Ok(new_fd) + } else { + let _ = close(new_fd, token); + let new_file = duplicate_file(fd, buf, false, token)?; - // Duplicate FIRST (fd may equal new_fd), then replace new_fd's descriptor. - let new_file = duplicate_file(fd, buf, false, token)?; - - let old_file = { let current_lock = context::current(); let mut current = current_lock.read(token.token()); - let (context, mut split_token) = current.token_split(); - - let old_file = context.remove_file(new_fd, &mut split_token); - - if context - .insert_file(new_fd, new_file, &mut split_token) - .is_none() - { - if let Some(old) = old_file { - context.insert_file(new_fd, old, &mut split_token); - } - return Err(Error::new(EMFILE)); - } - old_file - }; - - if let Some(old) = old_file { - let _ = old.close(token); + let (context, mut token) = current.token_split(); + context + .insert_file(new_fd, new_file, &mut token) + .ok_or(Error::new(EMFILE)) } - - Ok(new_fd) } pub fn call( fd: FileHandle,