diff --git a/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index 33062da55b9..46c06cafa2e 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.153 2026/03/03 09:57:25 dtucker Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.154 2026/05/31 04:59:51 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -1581,6 +1581,7 @@ process_extended_copy_data(uint32_t id) uint64_t len, read_off, read_len, write_off; int r, copy_until_eof, status = SSH2_FX_OP_UNSUPPORTED; size_t ret; + struct stat st_read, st_write; if ((r = get_handle(iqueue, &read_handle)) != 0 || (r = sshbuf_get_u64(iqueue, &read_off)) != 0 || @@ -1603,12 +1604,30 @@ process_extended_copy_data(uint32_t id) } else copy_until_eof = 0; + /* Disallow reading & writing to the same handle, path or inode */ read_fd = handle_to_fd(read_handle); write_fd = handle_to_fd(write_handle); - - /* Disallow reading & writing to the same handle or same path or dirs */ + if (fstat(read_fd, &st_read) != 0) { + status = errno_to_portable(errno); + error_f("fstat read_fd failed: %s", strerror(errno)); + goto out; + } + if (fstat(write_fd, &st_write) != 0) { + status = errno_to_portable(errno); + error_f("fstat write_fd failed: %s", strerror(errno)); + goto out; + } if (read_handle == write_handle || read_fd < 0 || write_fd < 0 || - !strcmp(handle_to_name(read_handle), handle_to_name(write_handle))) { + !strcmp(handle_to_name(read_handle), handle_to_name(write_handle)) || + (st_read.st_dev != 0 && st_read.st_ino != 0 && + st_read.st_dev == st_write.st_dev && + st_read.st_ino == st_write.st_ino)) { + error_f("refusing to read/write same file: " + "read \"%s\" dev %lu ino %lu, write \"%s\" dev %lu ino %lu", + handle_to_name(read_handle), + (u_long)st_read.st_dev, (u_long)st_read.st_ino, + handle_to_name(write_handle), + (u_long)st_write.st_dev, (u_long)st_write.st_ino); status = SSH2_FX_FAILURE; goto out; }