The vfs_getxattr_alloc() function currently returns a ssize_t value
despite the fact that it only uses int values internally for return
values. Fix this by converting vfs_getxattr_alloc() to return an
int type and adjust the callers as necessary. As part of these
caller modifications, some of the callers are fixed to properly free
the xattr value buffer on both success and failure to ensure that
memory is not leaked in the failure case.
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
* vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
*
* Allocate memory, if not already allocated, or re-allocate correct size,
* vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
*
* Allocate memory, if not already allocated, or re-allocate correct size,
- * before retrieving the extended attribute.
+ * before retrieving the extended attribute. The xattr value buffer should
+ * always be freed by the caller, even on error.
*
* Returns the result of alloc, if failed, or the getxattr operation.
*/
*
* Returns the result of alloc, if failed, or the getxattr operation.
*/
vfs_getxattr_alloc(struct user_namespace *mnt_userns, struct dentry *dentry,
const char *name, char **xattr_value, size_t xattr_size,
gfp_t flags)
vfs_getxattr_alloc(struct user_namespace *mnt_userns, struct dentry *dentry,
const char *name, char **xattr_value, size_t xattr_size,
gfp_t flags)
int vfs_removexattr(struct user_namespace *, struct dentry *, const char *);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
int vfs_removexattr(struct user_namespace *, struct dentry *, const char *);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
-ssize_t vfs_getxattr_alloc(struct user_namespace *mnt_userns,
- struct dentry *dentry, const char *name,
- char **xattr_value, size_t size, gfp_t flags);
+int vfs_getxattr_alloc(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
+ char **xattr_value, size_t size, gfp_t flags);
int xattr_supported_namespace(struct inode *inode, const char *prefix);
int xattr_supported_namespace(struct inode *inode, const char *prefix);
struct aa_profile *profile, unsigned int state)
{
int i;
struct aa_profile *profile, unsigned int state)
{
int i;
struct dentry *d;
char *value = NULL;
struct dentry *d;
char *value = NULL;
- int value_size = 0, ret = profile->xattr_count;
+ int size, value_size = 0, ret = profile->xattr_count;
if (!bprm || !profile->xattr_count)
return 0;
if (!bprm || !profile->xattr_count)
return 0;
return m & ~VFS_CAP_FLAGS_EFFECTIVE;
}
return m & ~VFS_CAP_FLAGS_EFFECTIVE;
}
-static bool is_v2header(size_t size, const struct vfs_cap_data *cap)
+static bool is_v2header(int size, const struct vfs_cap_data *cap)
{
if (size != XATTR_CAPS_SZ_2)
return false;
return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2;
}
{
if (size != XATTR_CAPS_SZ_2)
return false;
return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2;
}
-static bool is_v3header(size_t size, const struct vfs_cap_data *cap)
+static bool is_v3header(int size, const struct vfs_cap_data *cap)
{
if (size != XATTR_CAPS_SZ_3)
return false;
{
if (size != XATTR_CAPS_SZ_3)
return false;
struct inode *inode, const char *name, void **buffer,
bool alloc)
{
struct inode *inode, const char *name, void **buffer,
bool alloc)
{
kuid_t kroot;
u32 nsmagic, magic;
uid_t root, mappedroot;
kuid_t kroot;
u32 nsmagic, magic;
uid_t root, mappedroot;
dentry = d_find_any_alias(inode);
if (!dentry)
return -EINVAL;
dentry = d_find_any_alias(inode);
if (!dentry)
return -EINVAL;
-
- size = sizeof(struct vfs_ns_cap_data);
- ret = (int)vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS,
- &tmpbuf, size, GFP_NOFS);
+ size = vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS, &tmpbuf,
+ sizeof(struct vfs_ns_cap_data), GFP_NOFS);
-
- if (ret < 0 || !tmpbuf)
- return ret;
+ /* gcc11 complains if we don't check for !tmpbuf */
+ if (size < 0 || !tmpbuf)
+ goto out_free;
fs_ns = inode->i_sb->s_user_ns;
cap = (struct vfs_cap_data *) tmpbuf;
fs_ns = inode->i_sb->s_user_ns;
cap = (struct vfs_cap_data *) tmpbuf;
- if (is_v2header((size_t) ret, cap)) {
+ if (is_v2header(size, cap)) {
- } else if (is_v3header((size_t) ret, cap)) {
+ } else if (is_v3header(size, cap)) {
nscap = (struct vfs_ns_cap_data *) tmpbuf;
root = le32_to_cpu(nscap->rootid);
} else {
nscap = (struct vfs_ns_cap_data *) tmpbuf;
root = le32_to_cpu(nscap->rootid);
} else {
(char **)&xattr_data, 0, GFP_NOFS);
if (rc <= 0) {
if (rc == -ENODATA)
(char **)&xattr_data, 0, GFP_NOFS);
if (rc <= 0) {
if (rc == -ENODATA)
}
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
rc = 1;
else
rc = 0;
}
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
rc = 1;
else
rc = 0;
kfree(xattr_data);
return rc;
}
kfree(xattr_data);
return rc;
}
rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
0, GFP_NOFS);
rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
0, GFP_NOFS);
- if (rc < 0)
- return 1;
+ if (rc < 0) {
+ rc = 1;
+ goto out;
+ }
if (rc == xattr_value_len)
rc = !!memcmp(xattr_value, xattr_data, rc);
else
rc = 1;
if (rc == xattr_value_len)
rc = !!memcmp(xattr_value, xattr_data, rc);
else
rc = 1;
kfree(xattr_data);
return rc;
}
kfree(xattr_data);
return rc;
}
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
int ima_read_xattr(struct dentry *dentry,
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
int ima_read_xattr(struct dentry *dentry,
- struct evm_ima_xattr_data **xattr_value);
+ struct evm_ima_xattr_data **xattr_value, int xattr_len);
#else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
#else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
}
static inline int ima_read_xattr(struct dentry *dentry,
}
static inline int ima_read_xattr(struct dentry *dentry,
- struct evm_ima_xattr_data **xattr_value)
+ struct evm_ima_xattr_data **xattr_value,
+ int xattr_len)
}
int ima_read_xattr(struct dentry *dentry,
}
int ima_read_xattr(struct dentry *dentry,
- struct evm_ima_xattr_data **xattr_value)
+ struct evm_ima_xattr_data **xattr_value, int xattr_len)
ret = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_IMA,
ret = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_IMA,
- (char **)xattr_value, 0, GFP_NOFS);
+ (char **)xattr_value, xattr_len, GFP_NOFS);
if (ret == -EOPNOTSUPP)
ret = 0;
return ret;
if (ret == -EOPNOTSUPP)
ret = 0;
return ret;
/* HASH sets the digital signature and update flags, nothing else */
if ((action & IMA_HASH) &&
!(test_bit(IMA_DIGSIG, &iint->atomic_flags))) {
/* HASH sets the digital signature and update flags, nothing else */
if ((action & IMA_HASH) &&
!(test_bit(IMA_DIGSIG, &iint->atomic_flags))) {
- xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
+ xattr_len = ima_read_xattr(file_dentry(file),
+ &xattr_value, xattr_len);
if ((xattr_value && xattr_len > 2) &&
(xattr_value->type == EVM_IMA_XATTR_DIGSIG))
set_bit(IMA_DIGSIG, &iint->atomic_flags);
if ((xattr_value && xattr_len > 2) &&
(xattr_value->type == EVM_IMA_XATTR_DIGSIG))
set_bit(IMA_DIGSIG, &iint->atomic_flags);
if ((action & IMA_APPRAISE_SUBMASK) ||
strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) {
/* read 'security.ima' */
if ((action & IMA_APPRAISE_SUBMASK) ||
strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) {
/* read 'security.ima' */
- xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
+ xattr_len = ima_read_xattr(file_dentry(file),
+ &xattr_value, xattr_len);
/*
* Read the appended modsig if allowed by the policy, and allow
/*
* Read the appended modsig if allowed by the policy, and allow
rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file),
XATTR_NAME_EVM, (char **)&xattr_data, 0,
GFP_NOFS);
rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file),
XATTR_NAME_EVM, (char **)&xattr_data, 0,
GFP_NOFS);
- if (rc <= 0)
- return 0;
-
- if (xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
- kfree(xattr_data);
- return 0;
+ if (rc <= 0 || xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
+ rc = 0;
+ goto out;
}
rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
field_data);
}
rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
field_data);
kfree(xattr_data);
return rc;
}
kfree(xattr_data);
return rc;
}