From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2155) id ED5303857C71; Tue, 14 Sep 2021 15:04:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ED5303857C71 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Corinna Vinschen To: cygwin-cvs@sourceware.org Subject: [newlib-cygwin] Cygwin: fhandler_pipe: fix permission problem X-Act-Checkin: newlib-cygwin X-Git-Author: Ken Brown X-Git-Refname: refs/heads/master X-Git-Oldrev: d0ad52aa6eb1a47ebb76a11118b19aa70e2671f0 X-Git-Newrev: f56206cd86b9aa02668b858fa8067d74762edc58 Message-Id: <20210914150453.ED5303857C71@sourceware.org> Date: Tue, 14 Sep 2021 15:04:53 +0000 (GMT) X-BeenThere: cygwin-cvs@cygwin.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Cygwin core component git logs List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Sep 2021 15:04:54 -0000 https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=f56206cd86b9aa02668b858fa8067d74762edc58 commit f56206cd86b9aa02668b858fa8067d74762edc58 Author: Ken Brown Date: Sat May 25 10:27:38 2019 -0400 Cygwin: fhandler_pipe: fix permission problem The read handles of pipes created by CreateNamedPipe don't have FILE_WRITE_ATTRIBUTES access unless the pipe is created with PIPE_ACCESS_DUPLEX. This causes set_pipe_non_blocking to fail on such handles. To fix this, add a helper function nt_create, which uses NtCreateNamedPipeFile instead of CreateNamedPipe and gives us more flexibility in setting access rights. Use this helper function in fhandler_pipe::create (fhandler_pipe *[2], unsigned, int), which is the version of fhandler_pipe::create used by the pipe and pipe2 system calls. For convenience, also add a static member function fhandler_pipe::npfs_handle similar to those used by fhandler_fifo and fhandler_socket_unix. Diff: --- winsup/cygwin/fhandler.h | 1 + winsup/cygwin/fhandler_pipe.cc | 189 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 2 deletions(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 5873bb6be..f9fc9f360 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1267,6 +1267,7 @@ public: int __reg3 fadvise (off_t, off_t, int); int __reg3 ftruncate (off_t, bool); int init (HANDLE, DWORD, mode_t, int64_t); + static NTSTATUS npfs_handle (HANDLE &); static int create (fhandler_pipe *[2], unsigned, int); static DWORD create (LPSECURITY_ATTRIBUTES, HANDLE *, HANDLE *, DWORD, const char *, DWORD, int64_t *unique_id = NULL); diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc index 75435b009..4819fc580 100644 --- a/winsup/cygwin/fhandler_pipe.cc +++ b/winsup/cygwin/fhandler_pipe.cc @@ -35,7 +35,12 @@ fhandler_pipe::fhandler_pipe () need_fork_fixup (true); } -/* This also sets the pipe's read mode to byte_stream unconditionally. */ +/* The following function is intended for fhandler_pipe objects + created by the second version of fhandler_pipe::create below. See + the comment preceding the latter. + + In addition to setting the blocking mode of the pipe handle, it + also sets the pipe's read mode to byte_stream unconditionally. */ void fhandler_pipe::set_pipe_non_blocking (bool nonblocking) { @@ -566,6 +571,24 @@ fhandler_pipe::create (LPSECURITY_ATTRIBUTES sa_ptr, PHANDLE r, PHANDLE w, return 0; } +/* The next version of fhandler_pipe::create used to call the previous + version. But the read handle created by the latter doesn't have + FILE_WRITE_ATTRIBUTES access unless the pipe is created with + PIPE_ACCESS_DUPLEX, and it doesn't seem possible to add that access + right. This causes set_pipe_non_blocking to fail. + + To fix this we will define a helper function 'nt_create' that is + similar to the above fhandler_pipe::create but uses + NtCreateNamedPipeFile instead of CreateNamedPipe; this gives more + flexibility in setting the access rights. We will use this helper + function in the version of fhandler_pipe::create below, which + suffices for all of our uses of set_pipe_non_blocking. For + simplicity, nt_create will omit the 'open_mode' and 'name' + parameters, which aren't needed for our purposes. */ + +static int nt_create (LPSECURITY_ATTRIBUTES, PHANDLE, PHANDLE, DWORD, + int64_t *); + int fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode) { @@ -574,7 +597,7 @@ fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode) int res = -1; int64_t unique_id; - int ret = create (sa, &r, &w, psize, NULL, 0, &unique_id); + int ret = nt_create (sa, &r, &w, psize, &unique_id); if (ret) __seterrno_from_win_error (ret); else if ((fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev)) == NULL) @@ -601,6 +624,168 @@ fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode) return res; } +NTSTATUS +fhandler_pipe::npfs_handle (HANDLE &nph) +{ + static NO_COPY SRWLOCK npfs_lock; + static NO_COPY HANDLE npfs_dirh; + + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + + /* Lockless after first call. */ + if (npfs_dirh) + { + nph = npfs_dirh; + return STATUS_SUCCESS; + } + AcquireSRWLockExclusive (&npfs_lock); + if (!npfs_dirh) + { + InitializeObjectAttributes (&attr, &ro_u_npfs, 0, NULL, NULL); + status = NtOpenFile (&npfs_dirh, FILE_READ_ATTRIBUTES | SYNCHRONIZE, + &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0); + } + ReleaseSRWLockExclusive (&npfs_lock); + if (NT_SUCCESS (status)) + nph = npfs_dirh; + return status; +} + +static int +nt_create (LPSECURITY_ATTRIBUTES sa_ptr, PHANDLE r, PHANDLE w, + DWORD psize, int64_t *unique_id) +{ + NTSTATUS status; + HANDLE npfsh; + ACCESS_MASK access; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + LARGE_INTEGER timeout; + + /* Default to error. */ + if (r) + *r = NULL; + if (w) + *w = NULL; + + status = fhandler_pipe::npfs_handle (npfsh); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return GetLastError (); + } + + /* Ensure that there is enough pipe buffer space for atomic writes. */ + if (!psize) + psize = DEFAULT_PIPEBUFSIZE; + + UNICODE_STRING pipename; + WCHAR pipename_buf[MAX_PATH]; + size_t len = __small_swprintf (pipename_buf, L"%S-%u-", + &cygheap->installation_key, + GetCurrentProcessId ()); + + access = GENERIC_READ | FILE_WRITE_ATTRIBUTES; + + ULONG pipe_type = pipe_byte ? FILE_PIPE_BYTE_STREAM_TYPE + : FILE_PIPE_MESSAGE_TYPE; + + /* Retry NtCreateNamedPipeFile as long as the pipe name is in use. + Retrying will probably never be necessary, but we want + to be as robust as possible. */ + DWORD err = 0; + while (r && !*r) + { + static volatile ULONG pipe_unique_id; + LONG id = InterlockedIncrement ((LONG *) &pipe_unique_id); + __small_swprintf (pipename_buf + len, L"pipe-nt-%p", id); + if (unique_id) + *unique_id = ((int64_t) id << 32 | GetCurrentProcessId ()); + + debug_printf ("name %W, size %u, mode %s", pipename_buf, psize, + (pipe_type & FILE_PIPE_MESSAGE_TYPE) + ? "PIPE_TYPE_MESSAGE" : "PIPE_TYPE_BYTE"); + + RtlInitUnicodeString (&pipename, pipename_buf); + + InitializeObjectAttributes (&attr, &pipename, + sa_ptr->bInheritHandle ? OBJ_INHERIT : 0, + npfsh, sa_ptr->lpSecurityDescriptor); + + timeout.QuadPart = -500000; + status = NtCreateNamedPipeFile (r, access, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, 0, pipe_type, + FILE_PIPE_BYTE_STREAM_MODE, + 0, 1, psize, psize, &timeout); + + if (NT_SUCCESS (status)) + { + debug_printf ("pipe read handle %p", *r); + err = 0; + break; + } + + switch (status) + { + case STATUS_PIPE_BUSY: + case STATUS_INSTANCE_NOT_AVAILABLE: + case STATUS_PIPE_NOT_AVAILABLE: + /* The pipe is already open with compatible parameters. + Pick a new name and retry. */ + debug_printf ("pipe busy, retrying"); + *r = NULL; + break; + case STATUS_ACCESS_DENIED: + /* The pipe is already open with incompatible parameters. + Pick a new name and retry. */ + debug_printf ("pipe access denied, retrying"); + *r = NULL; + break; + default: + { + __seterrno_from_nt_status (status); + err = GetLastError (); + debug_printf ("failed, %E"); + *r = INVALID_HANDLE_VALUE; + } + } + } + + if (err) + { + *r = NULL; + return err; + } + + if (!w) + debug_printf ("pipe write handle NULL"); + else + { + debug_printf ("NtOpenFile: name %S", &pipename); + + access = GENERIC_WRITE | FILE_READ_ATTRIBUTES; + status = NtOpenFile (w, access, &attr, &io, 0, 0); + if (!NT_SUCCESS (status)) + { + DWORD err = GetLastError (); + debug_printf ("NtOpenFile failed, r %p, %E", r); + if (r) + CloseHandle (*r); + *w = NULL; + return err; + } + + debug_printf ("pipe write handle %p", *w); + } + + /* Success. */ + return 0; +} + int fhandler_pipe::ioctl (unsigned int cmd, void *p) {