Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[CIFS] Prevent OOPs when mounting with remote prefixpath.

Fixes OOPs with message 'kernel BUG at fs/cifs/cifs_dfs_ref.c:274!'.
Checks if the prefixpath in an accesible while we are still in cifs_mount
and fails with reporting a error if we can't access the prefixpath

Should fix Samba bugs 6086 and 5861 and kernel bug 12192

Signed-off-by: Igor Mammedov <niallain@gmail.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

authored by

Igor Mammedov and committed by
Steve French
e4cce94c e1f81c8a

+48 -2
+1
fs/cifs/cifsproto.h
··· 42 42 #define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current_fsuid())); 43 43 #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));} 44 44 extern char *build_path_from_dentry(struct dentry *); 45 + extern char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb); 45 46 extern char *build_wildcard_path_from_dentry(struct dentry *direntry); 46 47 /* extern void renew_parental_timestamps(struct dentry *direntry);*/ 47 48 extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
+45
fs/cifs/connect.c
··· 2180 2180 "mount option supported")); 2181 2181 } 2182 2182 2183 + static int 2184 + is_path_accessible(int xid, struct cifsTconInfo *tcon, 2185 + struct cifs_sb_info *cifs_sb, const char *full_path) 2186 + { 2187 + int rc; 2188 + __u64 inode_num; 2189 + FILE_ALL_INFO *pfile_info; 2190 + 2191 + rc = CIFSGetSrvInodeNumber(xid, tcon, full_path, &inode_num, 2192 + cifs_sb->local_nls, 2193 + cifs_sb->mnt_cifs_flags & 2194 + CIFS_MOUNT_MAP_SPECIAL_CHR); 2195 + if (rc != -EOPNOTSUPP) 2196 + return rc; 2197 + 2198 + pfile_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 2199 + if (pfile_info == NULL) 2200 + return -ENOMEM; 2201 + 2202 + rc = CIFSSMBQPathInfo(xid, tcon, full_path, pfile_info, 2203 + 0 /* not legacy */, cifs_sb->local_nls, 2204 + cifs_sb->mnt_cifs_flags & 2205 + CIFS_MOUNT_MAP_SPECIAL_CHR); 2206 + kfree(pfile_info); 2207 + return rc; 2208 + } 2209 + 2183 2210 int 2184 2211 cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 2185 2212 char *mount_data, const char *devname) ··· 2217 2190 struct cifsSesInfo *pSesInfo = NULL; 2218 2191 struct cifsTconInfo *tcon = NULL; 2219 2192 struct TCP_Server_Info *srvTcp = NULL; 2193 + char *full_path; 2220 2194 2221 2195 xid = GetXid(); 2222 2196 ··· 2453 2425 if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) 2454 2426 cifs_sb->rsize = min(cifs_sb->rsize, 2455 2427 (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); 2428 + 2429 + if (!rc && cifs_sb->prepathlen) { 2430 + /* build_path_to_root works only when we have a valid tcon */ 2431 + full_path = cifs_build_path_to_root(cifs_sb); 2432 + if (full_path == NULL) { 2433 + rc = -ENOMEM; 2434 + goto mount_fail_check; 2435 + } 2436 + rc = is_path_accessible(xid, tcon, cifs_sb, full_path); 2437 + if (rc) { 2438 + cERROR(1, ("Path %s in not accessible: %d", 2439 + full_path, rc)); 2440 + kfree(full_path); 2441 + goto mount_fail_check; 2442 + } 2443 + kfree(full_path); 2444 + } 2456 2445 2457 2446 /* volume_info->password is freed above when existing session found 2458 2447 (in which case it is not needed anymore) but when new sesion is created
+2 -2
fs/cifs/inode.c
··· 621 621 .lookup = cifs_lookup, 622 622 }; 623 623 624 - static char *build_path_to_root(struct cifs_sb_info *cifs_sb) 624 + char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) 625 625 { 626 626 int pplen = cifs_sb->prepathlen; 627 627 int dfsplen; ··· 678 678 return inode; 679 679 680 680 cifs_sb = CIFS_SB(inode->i_sb); 681 - full_path = build_path_to_root(cifs_sb); 681 + full_path = cifs_build_path_to_root(cifs_sb); 682 682 if (full_path == NULL) 683 683 return ERR_PTR(-ENOMEM); 684 684