From 2f05de4dbf9c04008175679a7a7b9e0cf422a2fa Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 19 Aug 2021 16:42:23 +0200 Subject: Cygwin: fix all usages of NtQueryDirectoryObject Due to reports on the Cygwin mailing list[1][2], it was uncovered that a NtOpenDirectoryObject/NtQueryDirectoryObject/NtClose sequence with NtQueryDirectoryObject iterating over the directory entries, one entry per invocation, is not running atomically. If new entries are inserted into the queried directory, other entries may be moved around and then accidentally show up twice while iterating. Change (almost) all NtQueryDirectoryObject invocations so that it gets a really big buffer (64K) and ideally fetches all entries at once. This appears to work atomically. "Almost" all, because fhandler_procsys::readdir can't be easily changed. [1] https://cygwin.com/pipermail/cygwin/2021-July/248998.html [2] https://cygwin.com/pipermail/cygwin/2021-August/249124.html Fixes: e9c8cb31930bd ("(format_proc_partitions): Revamp loop over existing harddisks by scanning the NT native \Device object directory and looking for Harddisk entries.") Fixes: a998dd7055766 ("Implement advisory file locking.") Fixes: 3b7cd74bfdf56 ("(winpids::enum_processes): Fetch Cygwin processes from listing of shared cygwin object dir in the native NT namespace.") Fixes: 0d6f2b0117aa7 ("syscalls.cc (sync_worker): Rewrite using native NT functions.") Signed-off-by: Corinna Vinschen --- winsup/cygwin/flock.cc | 52 +++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'winsup/cygwin/flock.cc') diff --git a/winsup/cygwin/flock.cc b/winsup/cygwin/flock.cc index ecd2293fa..bd7a16d91 100644 --- a/winsup/cygwin/flock.cc +++ b/winsup/cygwin/flock.cc @@ -596,34 +596,46 @@ lockf_t::from_obj_name (inode_t *node, lockf_t **head, const wchar_t *name) lockf_t * inode_t::get_all_locks_list () { - struct fdbi - { - DIRECTORY_BASIC_INFORMATION dbi; - WCHAR buf[2][NAME_MAX + 1]; - } f; + tmp_pathbuf tp; ULONG context; NTSTATUS status; + BOOLEAN restart = TRUE; + bool last_run = false; lockf_t newlock, *lock = i_all_lf; - for (BOOLEAN restart = TRUE; - NT_SUCCESS (status = NtQueryDirectoryObject (i_dir, &f, sizeof f, TRUE, - restart, &context, NULL)); - restart = FALSE) + PDIRECTORY_BASIC_INFORMATION dbi_buf = (PDIRECTORY_BASIC_INFORMATION) + tp.w_get (); + while (!last_run) { - if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR)) - continue; - f.dbi.ObjectName.Buffer[LOCK_OBJ_NAME_LEN] = L'\0'; - if (!newlock.from_obj_name (this, &i_all_lf, f.dbi.ObjectName.Buffer)) - continue; - if (lock - i_all_lf >= MAX_LOCKF_CNT) + status = NtQueryDirectoryObject (i_dir, dbi_buf, 65536, FALSE, restart, + &context, NULL); + if (!NT_SUCCESS (status)) { - system_printf ("Warning, can't handle more than %d locks per file.", - MAX_LOCKF_CNT); + debug_printf ("NtQueryDirectoryObject, status %y", status); break; } - if (lock > i_all_lf) - lock[-1].lf_next = lock; - new (lock++) lockf_t (newlock); + if (status != STATUS_MORE_ENTRIES) + last_run = true; + restart = FALSE; + for (PDIRECTORY_BASIC_INFORMATION dbi = dbi_buf; + dbi->ObjectName.Length > 0; + dbi++) + { + if (dbi->ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR)) + continue; + dbi->ObjectName.Buffer[LOCK_OBJ_NAME_LEN] = L'\0'; + if (!newlock.from_obj_name (this, &i_all_lf, dbi->ObjectName.Buffer)) + continue; + if (lock - i_all_lf >= MAX_LOCKF_CNT) + { + system_printf ("Warning, can't handle more than %d locks per file.", + MAX_LOCKF_CNT); + break; + } + if (lock > i_all_lf) + lock[-1].lf_next = lock; + new (lock++) lockf_t (newlock); + } } /* If no lock has been found, return NULL. */ if (lock == i_all_lf) -- cgit v1.2.3