6671726054
atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable posix_acl.a_refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. **Important note for maintainers: Some functions from refcount_t API defined in lib/refcount.c have different memory ordering guarantees than their atomic counterparts. The full comparison can be seen in https://lkml.org/lkml/2017/11/15/57 and it is hopefully soon in state to be merged to the documentation tree. Normally the differences should not matter since refcount_t provides enough guarantees to satisfy the refcounting use cases, but in some rare cases it might matter. Please double check that you don't have some undocumented memory guarantees for this variable usage. For the posix_acl.a_refcount it might make a difference in following places: - get_cached_acl(): increment in refcount_inc_not_zero() only guarantees control dependency on success vs. fully ordered atomic counterpart. However this operation is performed under rcu_read_lock(), so this should be fine. - posix_acl_release(): decrement in refcount_dec_and_test() only provides RELEASE ordering and control dependency on success vs. fully ordered atomic counterpart Suggested-by: Kees Cook <keescook@chromium.org> Reviewed-by: David Windsor <dwindsor@gmail.com> Reviewed-by: Hans Liljestrand <ishkamiel@gmail.com> Signed-off-by: Elena Reshetova <elena.reshetova@intel.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
124 lines
3.1 KiB
C
124 lines
3.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
File: linux/posix_acl.h
|
|
|
|
(C) 2002 Andreas Gruenbacher, <a.gruenbacher@computer.org>
|
|
*/
|
|
|
|
|
|
#ifndef __LINUX_POSIX_ACL_H
|
|
#define __LINUX_POSIX_ACL_H
|
|
|
|
#include <linux/bug.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/refcount.h>
|
|
#include <uapi/linux/posix_acl.h>
|
|
|
|
struct posix_acl_entry {
|
|
short e_tag;
|
|
unsigned short e_perm;
|
|
union {
|
|
kuid_t e_uid;
|
|
kgid_t e_gid;
|
|
};
|
|
};
|
|
|
|
struct posix_acl {
|
|
refcount_t a_refcount;
|
|
struct rcu_head a_rcu;
|
|
unsigned int a_count;
|
|
struct posix_acl_entry a_entries[0];
|
|
};
|
|
|
|
#define FOREACH_ACL_ENTRY(pa, acl, pe) \
|
|
for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; pa<pe; pa++)
|
|
|
|
|
|
/*
|
|
* Duplicate an ACL handle.
|
|
*/
|
|
static inline struct posix_acl *
|
|
posix_acl_dup(struct posix_acl *acl)
|
|
{
|
|
if (acl)
|
|
refcount_inc(&acl->a_refcount);
|
|
return acl;
|
|
}
|
|
|
|
/*
|
|
* Free an ACL handle.
|
|
*/
|
|
static inline void
|
|
posix_acl_release(struct posix_acl *acl)
|
|
{
|
|
if (acl && refcount_dec_and_test(&acl->a_refcount))
|
|
kfree_rcu(acl, a_rcu);
|
|
}
|
|
|
|
|
|
/* posix_acl.c */
|
|
|
|
extern void posix_acl_init(struct posix_acl *, int);
|
|
extern struct posix_acl *posix_acl_alloc(int, gfp_t);
|
|
extern int posix_acl_valid(struct user_namespace *, const struct posix_acl *);
|
|
extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
|
|
extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
|
|
extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
|
|
extern int __posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
|
|
extern int __posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
|
|
|
|
extern struct posix_acl *get_posix_acl(struct inode *, int);
|
|
extern int set_posix_acl(struct inode *, int, struct posix_acl *);
|
|
|
|
#ifdef CONFIG_FS_POSIX_ACL
|
|
extern int posix_acl_chmod(struct inode *, umode_t);
|
|
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
|
|
struct posix_acl **);
|
|
extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **);
|
|
|
|
extern int simple_set_acl(struct inode *, struct posix_acl *, int);
|
|
extern int simple_acl_create(struct inode *, struct inode *);
|
|
|
|
struct posix_acl *get_cached_acl(struct inode *inode, int type);
|
|
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
|
|
void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
|
|
void forget_cached_acl(struct inode *inode, int type);
|
|
void forget_all_cached_acls(struct inode *inode);
|
|
|
|
static inline void cache_no_acl(struct inode *inode)
|
|
{
|
|
inode->i_acl = NULL;
|
|
inode->i_default_acl = NULL;
|
|
}
|
|
#else
|
|
static inline int posix_acl_chmod(struct inode *inode, umode_t mode)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define simple_set_acl NULL
|
|
|
|
static inline int simple_acl_create(struct inode *dir, struct inode *inode)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void cache_no_acl(struct inode *inode)
|
|
{
|
|
}
|
|
|
|
static inline int posix_acl_create(struct inode *inode, umode_t *mode,
|
|
struct posix_acl **default_acl, struct posix_acl **acl)
|
|
{
|
|
*default_acl = *acl = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static inline void forget_all_cached_acls(struct inode *inode)
|
|
{
|
|
}
|
|
#endif /* CONFIG_FS_POSIX_ACL */
|
|
|
|
struct posix_acl *get_acl(struct inode *inode, int type);
|
|
|
|
#endif /* __LINUX_POSIX_ACL_H */
|