summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/attr.c2
-rw-r--r--fs/binfmt_misc.c3
-rw-r--r--fs/btrfs/inode.c4
-rw-r--r--fs/btrfs/lzo.c16
-rw-r--r--fs/crypto/policy.c2
-rw-r--r--fs/namespace.c4
-rw-r--r--fs/nfs/nfs4proc.c17
-rw-r--r--fs/overlayfs/super.c3
-rw-r--r--fs/smb/client/sess.c2
-rw-r--r--fs/smb/client/smb1transport.c28
-rw-r--r--fs/tracefs/event_inode.c28
-rw-r--r--fs/tracefs/internal.h4
-rw-r--r--fs/verity/measure.c15
-rw-r--r--fs/xfs/xfs_buf.c2
14 files changed, 101 insertions, 29 deletions
diff --git a/fs/attr.c b/fs/attr.c
index ded221defae6..fc5817c9f4e8 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
* covered by the open-time check because sys_truncate() takes a
* path, not an open file.
*/
- if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
+ if (IS_VERITY(inode))
return -EPERM;
error = inode_newsize_ok(inode, attr->ia_size);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 725b48ac5873..b7075e4df46f 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -937,7 +937,8 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
/* last one */ {""}
};
- if (WARN_ON(user_ns != current_user_ns()))
+ /* The fscontext fd may have been passed to another user namespace. */
+ if (user_ns != current_user_ns())
return -EINVAL;
/* Never exec off this instance and never let anything stack on it. */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dc5148f176e7..1271be0fbfcf 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4167,10 +4167,11 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path
btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item),
&inode->flags, &inode->ro_flags);
+
+cache_index:
btrfs_update_inode_mapping_flags(inode);
btrfs_set_inode_mapping_order(inode);
-cache_index:
/*
* If we were modified in the current generation and evicted from memory
* and then re-read we need to do a full sync since we don't have any
@@ -10193,6 +10194,7 @@ out_cb:
if (cb)
cleanup_compressed_bio(cb);
out:
+ extent_changeset_free(data_reserved);
if (ret >= 0)
iocb->ki_pos += encoded->len;
return ret;
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 6e4aa22853ab..2f0996692da0 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -552,17 +552,27 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
size_t max_segment_len = workspace_buf_length(fs_info);
int ret;
- if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2))
+ if (unlikely(srclen <= LZO_LEN * 2 ||
+ srclen > max_segment_len + LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
+ srclen, LZO_LEN * 2, max_segment_len + LZO_LEN * 2);
return -EUCLEAN;
+ }
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen))
+ if (unlikely(in_len != srclen)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu",
+ in_len, srclen);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen - LZO_LEN * 2))
+ if (unlikely(in_len != srclen - LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu",
+ in_len, srclen - LZO_LEN * 2);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
out_len = sectorsize;
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 9915e39362db..c80b24a941ad 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
return -EFAULT;
policy.version = version;
- if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+ if (!inode_owner_or_capable(file_mnt_idmap(filp), inode))
return -EACCES;
ret = mnt_want_write_file(filp);
diff --git a/fs/namespace.c b/fs/namespace.c
index 341ddd353b3a..841ef2612786 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4502,6 +4502,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
new_mnt = vfs_create_mount(fc);
if (IS_ERR(new_mnt))
return PTR_ERR(new_mnt);
+ if (new_mnt->mnt_sb->s_flags & SB_NOUSER) {
+ mntput(new_mnt);
+ return -EINVAL;
+ }
new_mnt->mnt_flags = mnt_flags;
new_path.dentry = dget(fc->root);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4db27f4eb01e..6142a7daf983 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -10330,6 +10330,7 @@ static void nfs41_free_stateid_release(void *calldata)
struct nfs_free_stateid_data *data = calldata;
struct nfs_client *clp = data->server->nfs_client;
+ nfs_sb_deactive(data->server->super);
nfs_put_client(clp);
kfree(calldata);
}
@@ -10368,17 +10369,22 @@ static int nfs41_free_stateid(struct nfs_server *server,
struct nfs_free_stateid_data *data;
struct rpc_task *task;
struct nfs_client *clp = server->nfs_client;
+ int ret = -EIO;
if (!refcount_inc_not_zero(&clp->cl_count))
- return -EIO;
+ return ret;
+ if (!nfs_sb_active(server->super))
+ goto out_put_clp;
nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID,
&task_setup.rpc_client, &msg);
dprintk("NFS call free_stateid %p\n", stateid);
data = kmalloc_obj(*data);
- if (!data)
- return -ENOMEM;
+ if (!data) {
+ ret = -ENOMEM;
+ goto out_put_server;
+ }
data->server = server;
nfs4_stateid_copy(&data->args.stateid, stateid);
@@ -10394,6 +10400,11 @@ static int nfs41_free_stateid(struct nfs_server *server,
rpc_put_task(task);
stateid->type = NFS4_FREED_STATEID_TYPE;
return 0;
+out_put_server:
+ nfs_sb_deactive(server->super);
+out_put_clp:
+ nfs_put_client(clp);
+ return ret;
}
static void
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 60f0b7ceef0a..60b808b85fc4 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1544,7 +1544,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
int err;
err = -EIO;
- if (WARN_ON(fc->user_ns != current_user_ns()))
+ /* The fscontext fd may have been passed to another user namespace. */
+ if (fc->user_ns != current_user_ns())
goto out_err;
ovl_set_d_op(sb);
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index de2012cc9cf3..7cf7dd104f7c 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -233,9 +233,9 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
&iface->sockaddr,
rc);
- kref_put(&iface->refcount, release_iface);
/* failure to add chan should increase weight */
iface->weight_fulfilled++;
+ kref_put(&iface->refcount, release_iface);
continue;
}
diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c
index 53abb29fe71b..966f2cf83a51 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -260,9 +260,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
goto out;
if (out_buf) {
- *pbytes_returned = resp_iov.iov_len;
- if (resp_iov.iov_len)
- memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
+ /* Use smbCalcSize() for both single- and multi-part T2 responses,
+ * both here and in coalesce_t2().
+ */
+ unsigned int copy_len;
+ if (WARN_ON_ONCE(!resp_iov.iov_base)) {
+ rc = -EIO;
+ goto out;
+ }
+ copy_len = smbCalcSize(resp_iov.iov_base);
+ if (copy_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "response size %u exceeds buffer\n",
+ copy_len);
+ rc = -ENOBUFS;
+ goto out;
+ }
+ *pbytes_returned = copy_len;
+ memcpy(out_buf, resp_iov.iov_base, copy_len);
}
out:
@@ -386,11 +400,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
}
put_bcc(byte_count, target_hdr);
- byte_count = *pdu_len;
- byte_count += total_in_src;
+ /* use smbCalcSize() rather than *pdu_len: the demux loop resets
+ * *pdu_len to each secondary's pdu_length, making it unreliable.
+ */
+ byte_count = smbCalcSize(target_hdr);
/* don't allow buffer to overflow */
if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
- cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
+ cifs_dbg(FYI, "coalesced size exceeds buffer size (%u)\n",
byte_count);
return -ENOBUFS;
}
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 26b6453de30e..498d316bf5d7 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -124,7 +124,17 @@ static inline void put_ei(struct eventfs_inode *ei)
static inline void free_ei(struct eventfs_inode *ei)
{
if (ei) {
+ /* The ei should have no children if it is being freed. */
+ WARN_ON_ONCE(!list_empty(&ei->children));
ei->is_freed = 1;
+ /*
+ * The SRCU iteration has a smp_rmb() to make sure it
+ * sees a child (that may have already been freed)
+ * before it reads is_free. If is_free is set, it must
+ * not use the child it acquired from ei->children, as
+ * the list may be used for SRCU.
+ */
+ smp_wmb();
put_ei(ei);
}
}
@@ -629,6 +639,20 @@ static int eventfs_iterate(struct file *file, struct dir_context *ctx)
list_for_each_entry_srcu(ei_child, &ei->children, list,
srcu_read_lock_held(&eventfs_srcu)) {
+ /*
+ * If the ei is being freed, then the ei->children may be
+ * being used as the rcu list, which means the next element
+ * may be garbage. The ei->is_free is set before switching
+ * the ei->children over to ei->rcu. The read memory barrier
+ * here makes sure the ei_child is read before is_free is
+ * updated.
+ *
+ * Matches the smp_wmb() in free_ei()
+ */
+ smp_rmb();
+ if (ei->is_freed)
+ return -EINVAL;
+
if (c > 0) {
c--;
continue;
@@ -824,7 +848,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
*/
static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
{
- struct eventfs_inode *ei_child;
+ struct eventfs_inode *ei_child, *tmp;
/*
* Check recursion depth. It should never be greater than 3:
@@ -837,7 +861,7 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
return;
/* search for nested folders or files */
- list_for_each_entry(ei_child, &ei->children, list)
+ list_for_each_entry_safe(ei_child, tmp, &ei->children, list)
eventfs_remove_rec(ei_child, level + 1);
list_del_rcu(&ei->list);
diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
index a4a7f8431aff..c61481d04c8e 100644
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -46,11 +46,11 @@ struct eventfs_attr {
* @ino: The saved inode number
*/
struct eventfs_inode {
+ struct list_head list;
union {
- struct list_head list;
+ struct list_head children;
struct rcu_head rcu;
};
- struct list_head children;
const struct eventfs_entry *entries;
const char *name;
struct eventfs_attr *entry_attrs;
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index 6a35623ebdf0..465ec3733a8a 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -122,11 +122,11 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di
{
struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p;
const struct inode *inode = file_inode(file);
- u32 dynptr_sz = __bpf_dynptr_size(digest_ptr);
+ u64 dynptr_sz = __bpf_dynptr_size(digest_ptr);
struct fsverity_digest *arg;
const struct fsverity_info *vi;
const struct fsverity_hash_alg *hash_alg;
- int out_digest_sz;
+ u64 out_digest_sz;
if (dynptr_sz < sizeof(struct fsverity_digest))
return -EINVAL;
@@ -144,17 +144,20 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di
hash_alg = vi->tree_params.hash_alg;
+ out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
+ if (out_digest_sz < hash_alg->digest_size)
+ return -EOVERFLOW;
+
arg->digest_algorithm = hash_alg - fsverity_hash_algs;
arg->digest_size = hash_alg->digest_size;
- out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
-
/* copy digest */
- memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz));
+ memcpy(arg->digest, vi->file_digest, hash_alg->digest_size);
/* fill the extra buffer with zeros */
if (out_digest_sz > hash_alg->digest_size)
- memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
+ memset(arg->digest + hash_alg->digest_size, 0,
+ out_digest_sz - hash_alg->digest_size);
return 0;
}
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 0cea458f1353..23bd81fbc278 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -114,7 +114,7 @@ xfs_buf_free(
vfree(bp->b_addr);
else if (bp->b_flags & _XBF_KMEM)
kfree(bp->b_addr);
- else
+ else if (bp->b_addr)
folio_put(virt_to_folio(bp->b_addr));
call_rcu(&bp->b_rcu, xfs_buf_free_callback);