Re: [PATCH 0/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
Linus Torvalds <[email protected]>
| Newsgroups | org.kernel.vger.linux-api,dev.linux.lists.patches,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.netdev,org.kvack.linux-mm |
|---|---|
| Message-ID | <CAHk-=wgPzxQL9Bdze=qnSa5v8A2D6cPaU0Bi=j4N38+ja1NkzQ@mail.gmail.com> |
On Wed, 3 Jun 2026 at 17:46, Askar Safin <[email protected]> wrote: > > For example, in vmsplice I do "CLASS(fd, f)(fd)" and then I pass > "fd" (i. e. integer) to "do_writev/do_readv". I don't know whether > this is okay to do so. Oh, good point. It's ok in the sense that it will work, and it's not really going to cause problems, but it does mean that the 'struct file' will be looked up twice. And *technically* it's a TOCTOU race, where the first time you look it up - in the vmsplice() wrapper - it could be one file, and you make decisions based on that. And then pass it off to do_writev(), and it will look it up again, and now it might be a different file. Does it *matter*? No. Even if the file changed, and is now something else, it's just going to be a different file that the user does writev() on. do_writev() will still do all the appropriate safety checks etc, so it doesn't really change anything. It just means that you could pass what you *think* is a pipe (because you did that + if (!get_pipe_info(fd_file(f), /* for_splice = */ false)) + return -EBADF; and by the time do_writev() then looks up the fd again it might be something else, and now the user used vmsplice() as a really odd way to write to a another non-pipe file instead. But the user could have done that with a regular writev(), so it's just the user being silly - not something that really confuses the kernel. Coimpletely harmless, in other words. But it would probably be *cleaner* to pass in the 'struct file *' pointer that you already looked up once instead, and use vfs_writev() instead of do_writev(). And I do suspect that the wrapper system call should use the same SYSCALL_DEFINE4(vmsplice, int, fd, .. that the original used. Because it somebody crazy had the high bits set in 'fd', the old vmsplice() system call didn't care, but your new emulation system call will actually see the high bits on a 64-bit architecture. Again - that doesn't actually *matter*, because "CLASS(fd)" takes an "int fd" and those high bits will be masked out at use time both in vmsplice() and in do_readv/writev(). So it won't affect any behavior, but it does look a bit odd in the conversion. And I already answered Christian wrt the change in behavior: I think RWF_NOWAIT should always be set on the writing side - because splice() never waited after it filled a pipe - and instead that SPLICE_F_NONBLOCK flag should be used before write to check for whether we'll wait *before* doing the write like it used to do with ret = wait_for_space(pipe, flags); in vmsplice_to_pipe(). (On the other side, vmsplice_from_pipe() used to do pipe_clear_nowait(), but I think that becomes a non-issue with the conversion to readv()). And once you need wait_for_space(), that probably means that the new vmsplice() wrapper simpler needs to remain inside fs/splice.c, and we just need to make vfs_readv/vfs_writev non-static. Linus