Fix memory and handle leaks due to fdopendir:
* dir.cc (closedir): Call global close instead of just releasing the fhandler. * fhandler_disk_file.cc (fhandler_disk_file::closedir): Don't close fhandler handle here, rely on global closedir doing the right thing. * fhandler_registry.cc (fhandler_registry::readdir): Also delete d_hash when closing registry key. (fhandler_registry::rewinddir): Ditto. Avoid infinite recursion in VirtualStore under UAC: * fhandler_registry.cc (VIRT_CLASSES_KEY_PREFIX): Define. (VIRT_CLASSES_KEY_SUFFIX): Ditto. (VIRT_CLASSES_KEY): Ditto. (VIRT_CLASSES_LINKTGT): Ditto. (fhandler_registry::exists): Return virt_symlink as file type if this is a Classes key under the VirtualStore. (fhandler_registry::fstat): Handle virt_symlink. (fhandler_registry::readdir): Return DT_LNK as d_type if this is a Classes key under the VirtualStore. (fhandler_registry::fill_filebuf): Handle Classes symlink. Handle user impersonation in /proc/registry access: * autoload.cc (RegOpenUserClassesRoot): Define. (RegOpenCurrentUser): Define. * fhandler_registry.cc (RegOpenUserClassesRoot): Declare function missing in w32api. (RegOpenCurrentUser): Ditto. (fetch_hkey): New function. (fhandler_registry::open): Call fetch_hkey to get root registry key. (open_key): Ditto.
This commit is contained in:
		| @@ -1,3 +1,36 @@ | ||||
| 2012-02-02  Corinna Vinschen  <corinna@vinschen.de> | ||||
|  | ||||
| 	Fix memory and handle leaks due to fdopendir: | ||||
| 	* dir.cc (closedir): Call global close instead of just releasing the | ||||
| 	fhandler. | ||||
| 	* fhandler_disk_file.cc (fhandler_disk_file::closedir): Don't close | ||||
| 	fhandler handle here, rely on global closedir doing the right thing. | ||||
| 	* fhandler_registry.cc (fhandler_registry::readdir): Also delete | ||||
| 	d_hash when closing registry key. | ||||
| 	(fhandler_registry::rewinddir): Ditto. | ||||
|  | ||||
| 	Avoid infinite recursion in VirtualStore under UAC: | ||||
| 	* fhandler_registry.cc (VIRT_CLASSES_KEY_PREFIX): Define. | ||||
| 	(VIRT_CLASSES_KEY_SUFFIX): Ditto. | ||||
| 	(VIRT_CLASSES_KEY): Ditto. | ||||
| 	(VIRT_CLASSES_LINKTGT): Ditto. | ||||
| 	(fhandler_registry::exists): Return virt_symlink as file type if | ||||
| 	this is a Classes key under the VirtualStore. | ||||
| 	(fhandler_registry::fstat): Handle virt_symlink. | ||||
| 	(fhandler_registry::readdir): Return DT_LNK as d_type if this is a | ||||
| 	Classes key under the VirtualStore. | ||||
| 	(fhandler_registry::fill_filebuf): Handle Classes symlink. | ||||
|  | ||||
| 	Handle user impersonation in /proc/registry access: | ||||
| 	* autoload.cc (RegOpenUserClassesRoot): Define. | ||||
| 	(RegOpenCurrentUser): Define. | ||||
| 	* fhandler_registry.cc (RegOpenUserClassesRoot): Declare function | ||||
| 	missing in w32api. | ||||
| 	(RegOpenCurrentUser): Ditto. | ||||
| 	(fetch_hkey): New function. | ||||
| 	(fhandler_registry::open): Call fetch_hkey to get root registry key. | ||||
| 	(open_key): Ditto. | ||||
|  | ||||
| 2012-02-01  Christopher Faylor  <me.cygwin2012@cgf.cx> | ||||
|  | ||||
| 	* fcntl.cc (fcntl64): Add introductory debug statement.  Call dup3 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| /* autoload.cc: all dynamic load stuff. | ||||
|  | ||||
|    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, | ||||
|    2009, 2010, 2011 Red Hat, Inc. | ||||
|    2009, 2010, 2011, 2012 Red Hat, Inc. | ||||
|  | ||||
| This file is part of Cygwin. | ||||
|  | ||||
| @@ -367,6 +367,8 @@ LoadDLLfunc (LsaOpenPolicy, 16, advapi32) | ||||
| LoadDLLfunc (LsaQueryInformationPolicy, 12, advapi32) | ||||
| LoadDLLfunc (LsaRetrievePrivateData, 12, advapi32) | ||||
| LoadDLLfunc (LsaStorePrivateData, 12, advapi32) | ||||
| LoadDLLfunc (RegOpenUserClassesRoot, 16, advapi32) | ||||
| LoadDLLfunc (RegOpenCurrentUser, 8, advapi32) | ||||
| LoadDLLfunc (RegCloseKey, 4, advapi32) | ||||
| LoadDLLfunc (RegCreateKeyExW, 36, advapi32) | ||||
| LoadDLLfunc (RegEnumKeyExW, 32, advapi32) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| /* dir.cc: Posix directory-related routines | ||||
|  | ||||
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2006, 2007, | ||||
|    2008, 2009, 2010 Red Hat, Inc. | ||||
|    2008, 2009, 2010, 2012 Red Hat, Inc. | ||||
|  | ||||
| This file is part of Cygwin. | ||||
|  | ||||
| @@ -11,6 +11,7 @@ details. */ | ||||
|  | ||||
| #include "winsup.h" | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #define _COMPILING_NEWLIB | ||||
| #include <dirent.h> | ||||
| @@ -266,8 +267,7 @@ closedir (DIR *dir) | ||||
|  | ||||
|   int res = ((fhandler_base *) dir->__fh)->closedir (dir); | ||||
|  | ||||
|   cygheap->fdtab.release (dir->__d_fd); | ||||
|  | ||||
|   close (dir->__d_fd); | ||||
|   free (dir->__d_dirname); | ||||
|   free (dir->__d_dirent); | ||||
|   free (dir); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| /* fhandler_disk_file.cc | ||||
|  | ||||
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, | ||||
|    2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. | ||||
|    2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. | ||||
|  | ||||
| This file is part of Cygwin. | ||||
|  | ||||
| @@ -2344,21 +2344,8 @@ int | ||||
| fhandler_disk_file::closedir (DIR *dir) | ||||
| { | ||||
|   int res = 0; | ||||
|   NTSTATUS status; | ||||
|  | ||||
|   delete d_mounts (dir); | ||||
|   if (!get_handle ()) | ||||
|     /* ignore */; | ||||
|   else if (get_handle () == INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|       set_errno (EBADF); | ||||
|       res = -1; | ||||
|     } | ||||
|   else if (!NT_SUCCESS (status = NtClose (get_handle ()))) | ||||
|     { | ||||
|       __seterrno_from_nt_status (status); | ||||
|       res = -1; | ||||
|     } | ||||
|   syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ()); | ||||
|   return res; | ||||
| } | ||||
|   | ||||
| @@ -32,6 +32,20 @@ details. */ | ||||
| static const _off_t REG_ENUM_VALUES_MASK = 0x8000000; | ||||
| static const _off_t REG_POSITION_MASK = 0xffff; | ||||
|  | ||||
| /* These key paths are used below whenever we return key information. | ||||
|    The problem is UAC virtualization when running an admin account with | ||||
|    restricted rights.  In that case the subkey "Classes" in the VirtualStore | ||||
|    points to the HKEY_CLASSES_ROOT key again.  If "Classes" is handled as a | ||||
|    normal subdirectory, applications recursing throught the directory | ||||
|    hirarchy will invariably run into an infinite recursion.  What we do here | ||||
|    is to handle the "Classes" subkey as a symlink to HKEY_CLASSES_ROOT.  This | ||||
|    avoids the infinite recursion, unless the application blindly follows | ||||
|    symlinks pointing to directories, in which case it's their own fault. */ | ||||
| #define VIRT_CLASSES_KEY_PREFIX "/VirtualStore/MACHINE/SOFTWARE" | ||||
| #define VIRT_CLASSES_KEY_SUFFIX "Classes" | ||||
| #define VIRT_CLASSES_KEY VIRT_CLASSES_KEY_PREFIX "/" VIRT_CLASSES_KEY_SUFFIX | ||||
| #define VIRT_CLASSES_LINKTGT "/proc/registry/HKEY_CLASSES_ROOT" | ||||
|  | ||||
| /* List of root keys in /proc/registry. | ||||
|  * Possibly we should filter out those not relevant to the flavour of Windows | ||||
|  * Cygwin is running on. | ||||
| @@ -63,6 +77,33 @@ static const HKEY registry_keys[] = | ||||
|  | ||||
| static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY); | ||||
|  | ||||
| extern "C" { | ||||
|   LONG WINAPI RegOpenUserClassesRoot (HANDLE, DWORD, REGSAM, PHKEY); | ||||
|   LONG WINAPI RegOpenCurrentUser (REGSAM, PHKEY); | ||||
| }; | ||||
|  | ||||
| /* Make sure to access the correct per-user HKCR and HKCU hives, even if | ||||
|    the current user is only impersonated in another user's session. */ | ||||
| static HKEY | ||||
| fetch_hkey (int idx) /* idx *must* be valid */ | ||||
| { | ||||
|   HKEY key; | ||||
|  | ||||
|   if (registry_keys[idx] == HKEY_CLASSES_ROOT) | ||||
|     { | ||||
|       if (RegOpenUserClassesRoot (cygheap->user.issetuid () | ||||
| 				  ? cygheap->user.imp_token () : hProcImpToken, | ||||
| 				  0, KEY_READ, &key) == ERROR_SUCCESS) | ||||
| 	return key; | ||||
|     } | ||||
|   else if (registry_keys[idx] == HKEY_CURRENT_USER) | ||||
|     { | ||||
|       if (RegOpenCurrentUser (KEY_READ, &key) == ERROR_SUCCESS) | ||||
| 	return key; | ||||
|     } | ||||
|   return registry_keys[idx]; | ||||
| } | ||||
|  | ||||
| /* These get added to each subdirectory in /proc/registry. | ||||
|  * If we wanted to implement writing, we could maybe add a '.writable' entry or | ||||
|  * suchlike. | ||||
| @@ -319,7 +360,14 @@ fhandler_registry::exists () | ||||
|       if (!val_only) | ||||
| 	hKey = open_key (path, KEY_READ, wow64, false); | ||||
|       if (hKey != (HKEY) INVALID_HANDLE_VALUE) | ||||
| 	{ | ||||
| 	  if (!strcasecmp (path + strlen (path) | ||||
| 			   - sizeof (VIRT_CLASSES_KEY) + 1, | ||||
| 			   VIRT_CLASSES_KEY)) | ||||
| 	    file_type = virt_symlink; | ||||
| 	  else | ||||
| 	    file_type = virt_directory; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  /* Key does not exist or open failed with EACCES, | ||||
| @@ -429,6 +477,9 @@ fhandler_registry::fstat (struct __stat64 *buf) | ||||
|     case virt_none: | ||||
|       set_errno (ENOENT); | ||||
|       return -1; | ||||
|     case virt_symlink: | ||||
|       buf->st_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | ||||
|       break; | ||||
|     case virt_directory: | ||||
|       buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; | ||||
|       break; | ||||
| @@ -550,7 +601,6 @@ fhandler_registry::readdir (DIR *dir, dirent *de) | ||||
| { | ||||
|   DWORD buf_size = NAME_MAX + 1; | ||||
|   wchar_t buf[buf_size]; | ||||
|   HANDLE handle; | ||||
|   const char *path = dir->__d_dirname + proc_len + 1 + prefix_len; | ||||
|   LONG error; | ||||
|   int res = ENMFILE; | ||||
| @@ -568,8 +618,7 @@ fhandler_registry::readdir (DIR *dir, dirent *de) | ||||
|     { | ||||
|       if (dir->__d_position != 0) | ||||
| 	goto out; | ||||
|       handle = open_key (path + 1, KEY_READ, wow64, false); | ||||
|       dir->__handle = handle; | ||||
|       dir->__handle = open_key (path + 1, KEY_READ, wow64, false); | ||||
|       if (dir->__handle == INVALID_HANDLE_VALUE) | ||||
| 	goto out; | ||||
|       dir->__d_internal = (unsigned) new __DIR_hash (); | ||||
| @@ -615,6 +664,7 @@ retry: | ||||
|     } | ||||
|   if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) | ||||
|     { | ||||
|       delete d_hash (dir); | ||||
|       RegCloseKey ((HKEY) dir->__handle); | ||||
|       dir->__handle = INVALID_HANDLE_VALUE; | ||||
|       if (error != ERROR_NO_MORE_ITEMS) | ||||
| @@ -646,6 +696,11 @@ retry: | ||||
|  | ||||
|   if (dir->__d_position & REG_ENUM_VALUES_MASK) | ||||
|     de->d_type = DT_REG; | ||||
|   else if (!strcasecmp (de->d_name, "Classes") | ||||
| 	   && !strcasecmp (path + strlen (path) | ||||
| 			   - sizeof (VIRT_CLASSES_KEY_PREFIX) + 1, | ||||
| 			   VIRT_CLASSES_KEY_PREFIX)) | ||||
|     de->d_type = DT_LNK; | ||||
|   else | ||||
|     de->d_type = DT_DIR; | ||||
|  | ||||
| @@ -678,6 +733,7 @@ fhandler_registry::rewinddir (DIR * dir) | ||||
| { | ||||
|   if (dir->__handle != INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|       delete d_hash (dir); | ||||
|       RegCloseKey ((HKEY) dir->__handle); | ||||
|       dir->__handle = INVALID_HANDLE_VALUE; | ||||
|     } | ||||
| @@ -766,7 +822,7 @@ fhandler_registry::open (int flags, mode_t mode) | ||||
| 	      } | ||||
| 	    else | ||||
| 	      { | ||||
| 		set_io_handle (registry_keys[i]); | ||||
| 		set_io_handle (fetch_hkey (i)); | ||||
| 		/* Marking as nohandle allows to call dup on pseudo registry | ||||
| 		   handles. */ | ||||
| 		nohandle (true); | ||||
| @@ -881,6 +937,16 @@ fhandler_registry::fill_filebuf () | ||||
|       error = RegQueryValueExW (handle, value_name, NULL, &type, NULL, &size); | ||||
|       if (error != ERROR_SUCCESS) | ||||
| 	{ | ||||
| 	  if (error == ERROR_INVALID_HANDLE | ||||
| 	      && !strcasecmp (get_name () + strlen (get_name ()) | ||||
| 			      - sizeof (VIRT_CLASSES_KEY) + 1, | ||||
| 			      VIRT_CLASSES_KEY)) | ||||
| 	    { | ||||
| 	      filesize = sizeof (VIRT_CLASSES_LINKTGT); | ||||
| 	      filebuf = (char *) cmalloc_abort (HEAP_BUF, filesize); | ||||
| 	      strcpy (filebuf, VIRT_CLASSES_LINKTGT); | ||||
| 	      return true; | ||||
| 	    } | ||||
| 	  if (error != ERROR_FILE_NOT_FOUND) | ||||
| 	    { | ||||
| 	      seterrno_from_win_error (__FILE__, __LINE__, error); | ||||
| @@ -1025,7 +1091,7 @@ open_key (const char *name, REGSAM access, DWORD wow64, bool isValue) | ||||
| 	{ | ||||
| 	  for (int i = 0; registry_listing[i]; i++) | ||||
| 	    if (strncasematch (anchor, registry_listing[i], name - anchor - 1)) | ||||
| 	      hKey = registry_keys[i]; | ||||
| 	      hKey = fetch_hkey (i); | ||||
| 	  if (hKey == (HKEY) INVALID_HANDLE_VALUE) | ||||
| 	    return hKey; | ||||
| 	  hParentKey = hKey; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user