Vulnerability: The cifs_lookup function in fs/cifs/dir.c in the Linux kernel before 3.2.10 allows local users to cause a denial of service (OOPS) via attempted access to a special file, as demonstrated by a FIFO.
Vulnerability ID: CVE-2012-1090
CWE: CWE-20
Severity score: 6.9
Project: https://github.com/torvalds/linux
Published: 2012-05-17
File: fs/cifs/dir.c
Commit message: cifs: fix dentry refcount leak when opening a FIFO on lookup

commit 5bccda0ebc7c0331b81ac47d39e4b920b198b2cd upstream.

The cifs code will attempt to open files on lookup under certain
circumstances. What happens though if we find that the file we opened
was actually a FIFO or other special file?

Currently, the open filehandle just ends up being leaked leading to
a dentry refcount mismatch and oops on umount. Fix this by having the
code close the filehandle on the server if it turns out not to be a
regular file. While we're at it, change this spaghetti if statement
into a switch too.

Reported-by: CAI Qian <caiqian@redhat.com>
Tested-by: CAI Qian <caiqian@redhat.com>
Reviewed-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Commit author: Jeff Layton

Vulnerable version (before fix):
 cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, 			 * If either that or op not supported returned, follow 			 * the normal lookup. 			 */ 			if ((rc == 0) || (rc == -ENOENT)) 				posix_open = true; 			else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP)) 				pTcon->broken_posix_open = true; 		} 		if (!posix_open) 			rc = cifs_get_inode_info_unix(&newInode, full_path,

Fixed version (after fix):
 cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, 			 * If either that or op not supported returned, follow 			 * the normal lookup. 			 */ 			switch (rc) { 			case 0: 				/* 				 * The server may allow us to open things like 				 * FIFOs, but the client isn't set up to deal 				 * with that. If it's not a regular file, just 				 * close it and proceed as if it were a normal 				 * lookup. 				 */ 				if (newInode && !S_ISREG(newInode->i_mode)) { 					CIFSSMBClose(xid, pTcon, fileHandle); 					break; 				} 			case -ENOENT: 				posix_open = true; 			case -EOPNOTSUPP: 				break; 			default: 				pTcon->broken_posix_open = true; 			} 		} 		if (!posix_open) 			rc = cifs_get_inode_info_unix(&newInode, full_path,

Patch:
patch: @@ -584,10 +584,26 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 			 * If either that or op not supported returned, follow
 			 * the normal lookup.
 			 */
-			if ((rc == 0) || (rc == -ENOENT))
+			switch (rc) {
+			case 0:
+				/*
+				 * The server may allow us to open things like
+				 * FIFOs, but the client isn't set up to deal
+				 * with that. If it's not a regular file, just
+				 * close it and proceed as if it were a normal
+				 * lookup.
+				 */
+				if (newInode && !S_ISREG(newInode->i_mode)) {
+					CIFSSMBClose(xid, pTcon, fileHandle);
+					break;
+				}
+			case -ENOENT:
 				posix_open = true;
-			else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
+			case -EOPNOTSUPP:
+				break;
+			default:
 				pTcon->broken_posix_open = true;
+			}
 		}
 		if (!posix_open)
 			rc = cifs_get_inode_info_unix(&newInode, full_path,
path: fs/cifs/dir.c