2023-07-14 21:44 GMT+09:00, Tom Talpey <tom@xxxxxxxxxx>: > On 7/12/2023 7:40 PM, Namjae Jeon wrote: >> 2023-07-13 2:23 GMT+09:00, Tom Talpey <tom@xxxxxxxxxx>: >>> On 7/11/2023 8:30 AM, Namjae Jeon wrote: >>>> Since commit 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent >>>> and ->d_name"), >>>> ksmbd can not lookup cross mount points. If last component is a cross >>>> mount point during path lookup, check if it is crossed to follow it >>>> down. >>> >>> I actually thought not crossing mount points was intended, since >>> semantics can shift if the crossing changes filesystems or fs types. >>> >>> Does this change somehow prevent walking out of a mount via embedded >>> "/../" in the path? It's not obvious to me whether >>> vfs_path_parent_lookup >>> denies this. >> Yes, LOOKUP_BENEATH flags protect to lookup out of share. >>> >>> Tom. >>> >>> I'm not familiar enough with the VFS protection, but does this >> I have checked it before and now again. There is no problem. > > I'm still concerned about this. Crossing mount points in the server is > completely invisible to the client, yet the semantics change radically. Have you checked it with samba server ? This is exactly the same behavior as samba server. And users are also wanting exactly the same behavior as before. https://github.com/namjaejeon/ksmbd/issues/436 > > What if someone shares "/mnt", and proceeds to mount an ext4, a USB > FAT32, a CD-Rom, and an NFS filesystem in subdirectories? The client > has no clue, and will walk into them all, finding a very different > world in each. > > How will Posix extensions work across this? Case sensitivity/preserving? > Caching and locks/leases? I didn't understand exactly your question. And NFS also supports crossmnt, and we will be able to add 'crossmnt' parameter in smb.conf and work crossmnt by default. Thanks. > > Tom. >> Thanks! >>>> >>>> Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and >>>> ->d_name") >>>> Signed-off-by: Namjae Jeon <linkinjeon@xxxxxxxxxx> >>>> --- >>>> fs/smb/server/smb2pdu.c | 27 ++++++++++++-------- >>>> fs/smb/server/vfs.c | 56 >>>> +++++++++++++++++++++++------------------ >>>> fs/smb/server/vfs.h | 4 +-- >>>> 3 files changed, 49 insertions(+), 38 deletions(-) >>>> >>>> diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c >>>> index cf8822103f50..ca276634fd58 100644 >>>> --- a/fs/smb/server/smb2pdu.c >>>> +++ b/fs/smb/server/smb2pdu.c >>>> @@ -2467,8 +2467,9 @@ static void smb2_update_xattrs(struct >>>> ksmbd_tree_connect *tcon, >>>> } >>>> } >>>> >>>> -static int smb2_creat(struct ksmbd_work *work, struct path *path, char >>>> *name, >>>> - int open_flags, umode_t posix_mode, bool is_dir) >>>> +static int smb2_creat(struct ksmbd_work *work, struct path >>>> *parent_path, >>>> + struct path *path, char *name, int open_flags, >>>> + umode_t posix_mode, bool is_dir) >>>> { >>>> struct ksmbd_tree_connect *tcon = work->tcon; >>>> struct ksmbd_share_config *share = tcon->share_conf; >>>> @@ -2495,7 +2496,7 @@ static int smb2_creat(struct ksmbd_work *work, >>>> struct path *path, char *name, >>>> return rc; >>>> } >>>> >>>> - rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0); >>>> + rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); >>>> if (rc) { >>>> pr_err("cannot get linux path (%s), err = %d\n", >>>> name, rc); >>>> @@ -2565,7 +2566,7 @@ int smb2_open(struct ksmbd_work *work) >>>> struct ksmbd_tree_connect *tcon = work->tcon; >>>> struct smb2_create_req *req; >>>> struct smb2_create_rsp *rsp; >>>> - struct path path; >>>> + struct path path, parent_path; >>>> struct ksmbd_share_config *share = tcon->share_conf; >>>> struct ksmbd_file *fp = NULL; >>>> struct file *filp = NULL; >>>> @@ -2786,7 +2787,8 @@ int smb2_open(struct ksmbd_work *work) >>>> goto err_out1; >>>> } >>>> >>>> - rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, >>>> &path, >>>> 1); >>>> + rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, >>>> + &parent_path, &path, 1); >>>> if (!rc) { >>>> file_present = true; >>>> >>>> @@ -2906,7 +2908,8 @@ int smb2_open(struct ksmbd_work *work) >>>> >>>> /*create file if not present */ >>>> if (!file_present) { >>>> - rc = smb2_creat(work, &path, name, open_flags, posix_mode, >>>> + rc = smb2_creat(work, &parent_path, &path, name, open_flags, >>>> + posix_mode, >>>> req->CreateOptions & FILE_DIRECTORY_FILE_LE); >>>> if (rc) { >>>> if (rc == -ENOENT) { >>>> @@ -3321,8 +3324,9 @@ int smb2_open(struct ksmbd_work *work) >>>> >>>> err_out: >>>> if (file_present || created) { >>>> - inode_unlock(d_inode(path.dentry->d_parent)); >>>> - dput(path.dentry); >>>> + inode_unlock(d_inode(parent_path.dentry)); >>>> + path_put(&path); >>>> + path_put(&parent_path); >>>> } >>>> ksmbd_revert_fsids(work); >>>> err_out1: >>>> @@ -5545,7 +5549,7 @@ static int smb2_create_link(struct ksmbd_work >>>> *work, >>>> struct nls_table *local_nls) >>>> { >>>> char *link_name = NULL, *target_name = NULL, *pathname = NULL; >>>> - struct path path; >>>> + struct path path, parent_path; >>>> bool file_present = false; >>>> int rc; >>>> >>>> @@ -5575,7 +5579,7 @@ static int smb2_create_link(struct ksmbd_work >>>> *work, >>>> >>>> ksmbd_debug(SMB, "target name is %s\n", target_name); >>>> rc = ksmbd_vfs_kern_path_locked(work, link_name, >>>> LOOKUP_NO_SYMLINKS, >>>> - &path, 0); >>>> + &parent_path, &path, 0); >>>> if (rc) { >>>> if (rc != -ENOENT) >>>> goto out; >>>> @@ -5605,8 +5609,9 @@ static int smb2_create_link(struct ksmbd_work >>>> *work, >>>> rc = -EINVAL; >>>> out: >>>> if (file_present) { >>>> - inode_unlock(d_inode(path.dentry->d_parent)); >>>> + inode_unlock(d_inode(parent_path.dentry)); >>>> path_put(&path); >>>> + path_put(&parent_path); >>>> } >>>> if (!IS_ERR(link_name)) >>>> kfree(link_name); >>>> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c >>>> index e35914457350..fd06d267de61 100644 >>>> --- a/fs/smb/server/vfs.c >>>> +++ b/fs/smb/server/vfs.c >>>> @@ -63,13 +63,13 @@ int ksmbd_vfs_lock_parent(struct dentry *parent, >>>> struct dentry *child) >>>> >>>> static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config >>>> *share_conf, >>>> char *pathname, unsigned int flags, >>>> + struct path *parent_path, >>>> struct path *path) >>>> { >>>> struct qstr last; >>>> struct filename *filename; >>>> struct path *root_share_path = &share_conf->vfs_path; >>>> int err, type; >>>> - struct path parent_path; >>>> struct dentry *d; >>>> >>>> if (pathname[0] == '\0') { >>>> @@ -84,7 +84,7 @@ static int ksmbd_vfs_path_lookup_locked(struct >>>> ksmbd_share_config *share_conf, >>>> return PTR_ERR(filename); >>>> >>>> err = vfs_path_parent_lookup(filename, flags, >>>> - &parent_path, &last, &type, >>>> + parent_path, &last, &type, >>>> root_share_path); >>>> if (err) { >>>> putname(filename); >>>> @@ -92,13 +92,13 @@ static int ksmbd_vfs_path_lookup_locked(struct >>>> ksmbd_share_config *share_conf, >>>> } >>>> >>>> if (unlikely(type != LAST_NORM)) { >>>> - path_put(&parent_path); >>>> + path_put(parent_path); >>>> putname(filename); >>>> return -ENOENT; >>>> } >>>> >>>> - inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); >>>> - d = lookup_one_qstr_excl(&last, parent_path.dentry, 0); >>>> + inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); >>>> + d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); >>>> if (IS_ERR(d)) >>>> goto err_out; >>>> >>>> @@ -108,15 +108,20 @@ static int ksmbd_vfs_path_lookup_locked(struct >>>> ksmbd_share_config *share_conf, >>>> } >>>> >>>> path->dentry = d; >>>> - path->mnt = share_conf->vfs_path.mnt; >>>> - path_put(&parent_path); >>>> - putname(filename); >>>> + path->mnt = mntget(parent_path->mnt); >>>> + >>>> + err = follow_down(path, 0); >>>> + if (err < 0) { >>>> + path_put(path); >>>> + goto err_out; >>>> + } >>>> >>>> + putname(filename); >>>> return 0; >>>> >>>> err_out: >>>> - inode_unlock(parent_path.dentry->d_inode); >>>> - path_put(&parent_path); >>>> + inode_unlock(d_inode(parent_path->dentry)); >>>> + path_put(parent_path); >>>> putname(filename); >>>> return -ENOENT; >>>> } >>>> @@ -1194,14 +1199,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct >>>> path *dir, char *name, >>>> * Return: 0 on success, otherwise error >>>> */ >>>> int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, >>>> - unsigned int flags, struct path *path, >>>> - bool caseless) >>>> + unsigned int flags, struct path *parent_path, >>>> + struct path *path, bool caseless) >>>> { >>>> struct ksmbd_share_config *share_conf = work->tcon->share_conf; >>>> int err; >>>> - struct path parent_path; >>>> >>>> - err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path); >>>> + err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, >>>> parent_path, >>>> + path); >>>> if (!err) >>>> return 0; >>>> >>>> @@ -1216,10 +1221,10 @@ int ksmbd_vfs_kern_path_locked(struct >>>> ksmbd_work >>>> *work, char *name, >>>> path_len = strlen(filepath); >>>> remain_len = path_len; >>>> >>>> - parent_path = share_conf->vfs_path; >>>> - path_get(&parent_path); >>>> + *parent_path = share_conf->vfs_path; >>>> + path_get(parent_path); >>>> >>>> - while (d_can_lookup(parent_path.dentry)) { >>>> + while (d_can_lookup(parent_path->dentry)) { >>>> char *filename = filepath + path_len - remain_len; >>>> char *next = strchrnul(filename, '/'); >>>> size_t filename_len = next - filename; >>>> @@ -1228,7 +1233,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work >>>> *work, char *name, >>>> if (filename_len == 0) >>>> break; >>>> >>>> - err = ksmbd_vfs_lookup_in_dir(&parent_path, filename, >>>> + err = ksmbd_vfs_lookup_in_dir(parent_path, filename, >>>> filename_len, >>>> work->conn->um); >>>> if (err) >>>> @@ -1245,8 +1250,8 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work >>>> *work, char *name, >>>> goto out2; >>>> else if (is_last) >>>> goto out1; >>>> - path_put(&parent_path); >>>> - parent_path = *path; >>>> + path_put(parent_path); >>>> + *parent_path = *path; >>>> >>>> next[0] = '/'; >>>> remain_len -= filename_len + 1; >>>> @@ -1254,16 +1259,17 @@ int ksmbd_vfs_kern_path_locked(struct >>>> ksmbd_work >>>> *work, char *name, >>>> >>>> err = -EINVAL; >>>> out2: >>>> - path_put(&parent_path); >>>> + path_put(parent_path); >>>> out1: >>>> kfree(filepath); >>>> } >>>> >>>> if (!err) { >>>> - err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry); >>>> - if (err) >>>> - dput(path->dentry); >>>> - path_put(&parent_path); >>>> + err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); >>>> + if (err) { >>>> + path_put(path); >>>> + path_put(parent_path); >>>> + } >>>> } >>>> return err; >>>> } >>>> diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h >>>> index 80039312c255..72f9fb4b48d1 100644 >>>> --- a/fs/smb/server/vfs.h >>>> +++ b/fs/smb/server/vfs.h >>>> @@ -115,8 +115,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, >>>> char **xattr_stream_name, >>>> int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, >>>> const struct path *path, char *attr_name); >>>> int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, >>>> - unsigned int flags, struct path *path, >>>> - bool caseless); >>>> + unsigned int flags, struct path *parent_path, >>>> + struct path *path, bool caseless); >>>> struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, >>>> const char *name, >>>> unsigned int flags, >>> >> >