summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--security/apparmor/domain.c2
-rw-r--r--security/apparmor/file.c21
-rw-r--r--security/apparmor/include/file.h2
-rw-r--r--security/apparmor/include/path.h3
-rw-r--r--security/apparmor/lsm.c50
-rw-r--r--security/apparmor/mount.c22
6 files changed, 62 insertions, 38 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 0da0b2b96972..51b3143ec256 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -896,7 +896,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
ctx->nnp = aa_get_label(label);
/* buffer freed below, name is pointer into buffer */
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer) {
error = -ENOMEM;
goto done;
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 37d62ecec29d..b520fdfc3504 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -336,7 +336,7 @@ int aa_path_perm(const char *op, struct aa_label *label,
flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR :
0);
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
error = fn_for_each_confined(label, profile,
@@ -481,8 +481,8 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
int error;
/* buffer freed below, lname is pointer in buffer */
- buffer = aa_get_buffer();
- buffer2 = aa_get_buffer();
+ buffer = aa_get_buffer(false);
+ buffer2 = aa_get_buffer(false);
error = -ENOMEM;
if (!buffer || !buffer2)
goto out;
@@ -519,7 +519,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
static int __file_path_perm(const char *op, struct aa_label *label,
struct aa_label *flabel, struct file *file,
- u32 request, u32 denied)
+ u32 request, u32 denied, bool in_atomic)
{
struct aa_profile *profile;
struct aa_perms perms = {};
@@ -536,7 +536,7 @@ static int __file_path_perm(const char *op, struct aa_label *label,
return 0;
flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(in_atomic);
if (!buffer)
return -ENOMEM;
@@ -604,11 +604,12 @@ static int __file_sock_perm(const char *op, struct aa_label *label,
* @label: label being enforced (NOT NULL)
* @file: file to revalidate access permissions on (NOT NULL)
* @request: requested permissions
+ * @in_atomic: whether allocations need to be done in atomic context
*
* Returns: %0 if access allowed else error
*/
int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
- u32 request)
+ u32 request, bool in_atomic)
{
struct aa_file_ctx *fctx;
struct aa_label *flabel;
@@ -641,7 +642,7 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry))
error = __file_path_perm(op, label, flabel, file, request,
- denied);
+ denied, in_atomic);
else if (S_ISSOCK(file_inode(file)->i_mode))
error = __file_sock_perm(op, label, flabel, file, request,
@@ -669,7 +670,8 @@ static void revalidate_tty(struct aa_label *label)
struct tty_file_private, list);
file = file_priv->file;
- if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE))
+ if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE,
+ IN_ATOMIC))
drop_tty = 1;
}
spin_unlock(&tty->files_lock);
@@ -683,7 +685,8 @@ static int match_file(const void *p, struct file *file, unsigned int fd)
{
struct aa_label *label = (struct aa_label *)p;
- if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file)))
+ if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file),
+ IN_ATOMIC))
return fd + 1;
return 0;
}
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 8be09208cf7c..67fadf06fa73 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -201,7 +201,7 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry);
int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
- u32 request);
+ u32 request, bool in_atomic);
void aa_inherit_files(const struct cred *cred, struct files_struct *files);
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index b0b2ab85e42d..d2ab8a932bad 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -29,7 +29,8 @@ int aa_path_name(const struct path *path, int flags, char *buffer,
const char **name, const char **info,
const char *disconnected);
-char *aa_get_buffer(void);
+#define IN_ATOMIC true
+char *aa_get_buffer(bool in_atomic);
void aa_put_buffer(char *buf);
#endif /* __AA_PATH_H */
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3e0cfdebee45..c940e8988444 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -53,6 +53,10 @@ union aa_buffer {
char buffer[1];
};
+#define RESERVE_COUNT 2
+static int reserve_count = RESERVE_COUNT;
+static int buffer_count;
+
static LIST_HEAD(aa_global_buffers);
static DEFINE_SPINLOCK(aa_buffers_lock);
@@ -452,7 +456,8 @@ static void apparmor_file_free_security(struct file *file)
aa_put_label(rcu_access_pointer(ctx->label));
}
-static int common_file_perm(const char *op, struct file *file, u32 mask)
+static int common_file_perm(const char *op, struct file *file, u32 mask,
+ bool in_atomic)
{
struct aa_label *label;
int error = 0;
@@ -462,7 +467,7 @@ static int common_file_perm(const char *op, struct file *file, u32 mask)
return -EACCES;
label = __begin_current_label_crit_section();
- error = aa_file_perm(op, label, file, mask);
+ error = aa_file_perm(op, label, file, mask, in_atomic);
__end_current_label_crit_section(label);
return error;
@@ -470,12 +475,13 @@ static int common_file_perm(const char *op, struct file *file, u32 mask)
static int apparmor_file_receive(struct file *file)
{
- return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file));
+ return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file),
+ false);
}
static int apparmor_file_permission(struct file *file, int mask)
{
- return common_file_perm(OP_FPERM, file, mask);
+ return common_file_perm(OP_FPERM, file, mask, false);
}
static int apparmor_file_lock(struct file *file, unsigned int cmd)
@@ -485,11 +491,11 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
if (cmd == F_WRLCK)
mask |= MAY_WRITE;
- return common_file_perm(OP_FLOCK, file, mask);
+ return common_file_perm(OP_FLOCK, file, mask, false);
}
static int common_mmap(const char *op, struct file *file, unsigned long prot,
- unsigned long flags)
+ unsigned long flags, bool in_atomic)
{
int mask = 0;
@@ -507,20 +513,21 @@ static int common_mmap(const char *op, struct file *file, unsigned long prot,
if (prot & PROT_EXEC)
mask |= AA_EXEC_MMAP;
- return common_file_perm(op, file, mask);
+ return common_file_perm(op, file, mask, in_atomic);
}
static int apparmor_mmap_file(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
{
- return common_mmap(OP_FMMAP, file, prot, flags);
+ return common_mmap(OP_FMMAP, file, prot, flags, GFP_ATOMIC);
}
static int apparmor_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
{
return common_mmap(OP_FMPROT, vma->vm_file, prot,
- !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
+ !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0,
+ false);
}
static int apparmor_sb_mount(const char *dev_name, const struct path *path,
@@ -1571,24 +1578,36 @@ static int param_set_mode(const char *val, const struct kernel_param *kp)
return 0;
}
-char *aa_get_buffer(void)
+char *aa_get_buffer(bool in_atomic)
{
union aa_buffer *aa_buf;
bool try_again = true;
+ gfp_t flags = (GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
retry:
spin_lock(&aa_buffers_lock);
- if (!list_empty(&aa_global_buffers)) {
+ if (buffer_count > reserve_count ||
+ (in_atomic && !list_empty(&aa_global_buffers))) {
aa_buf = list_first_entry(&aa_global_buffers, union aa_buffer,
list);
list_del(&aa_buf->list);
+ buffer_count--;
spin_unlock(&aa_buffers_lock);
return &aa_buf->buffer[0];
}
+ if (in_atomic) {
+ /*
+ * out of reserve buffers and in atomic context so increase
+ * how many buffers to keep in reserve
+ */
+ reserve_count++;
+ flags = GFP_ATOMIC;
+ }
spin_unlock(&aa_buffers_lock);
- aa_buf = kmalloc(aa_g_path_max, GFP_KERNEL | __GFP_RETRY_MAYFAIL |
- __GFP_NOWARN);
+ if (!in_atomic)
+ might_sleep();
+ aa_buf = kmalloc(aa_g_path_max, flags);
if (!aa_buf) {
if (try_again) {
try_again = false;
@@ -1610,6 +1629,7 @@ void aa_put_buffer(char *buf)
spin_lock(&aa_buffers_lock);
list_add(&aa_buf->list, &aa_global_buffers);
+ buffer_count++;
spin_unlock(&aa_buffers_lock);
}
@@ -1661,9 +1681,9 @@ static int __init alloc_buffers(void)
* disabled early at boot if aa_g_path_max is extremly high.
*/
if (num_online_cpus() > 1)
- num = 4;
+ num = 4 + RESERVE_COUNT;
else
- num = 2;
+ num = 2 + RESERVE_COUNT;
for (i = 0; i < num; i++) {
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 2dbccb021663..b06f5cba8fc4 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -412,7 +412,7 @@ int aa_remount(struct aa_label *label, const struct path *path,
binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
error = fn_for_each_confined(label, profile,
@@ -443,8 +443,8 @@ int aa_bind_mount(struct aa_label *label, const struct path *path,
if (error)
return error;
- buffer = aa_get_buffer();
- old_buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
+ old_buffer = aa_get_buffer(false);
error = -ENOMEM;
if (!buffer || old_buffer)
goto out;
@@ -474,7 +474,7 @@ int aa_mount_change_type(struct aa_label *label, const struct path *path,
flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
MS_UNBINDABLE);
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
error = fn_for_each_confined(label, profile,
@@ -503,8 +503,8 @@ int aa_move_mount(struct aa_label *label, const struct path *path,
if (error)
return error;
- buffer = aa_get_buffer();
- old_buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
+ old_buffer = aa_get_buffer(false);
error = -ENOMEM;
if (!buffer || !old_buffer)
goto out;
@@ -554,13 +554,13 @@ int aa_new_mount(struct aa_label *label, const char *dev_name,
}
}
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer) {
error = -ENOMEM;
goto out;
}
if (dev_path) {
- dev_buffer = aa_get_buffer();
+ dev_buffer = aa_get_buffer(false);
if (!dev_buffer) {
error = -ENOMEM;
goto out;
@@ -624,7 +624,7 @@ int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
AA_BUG(!label);
AA_BUG(!mnt);
- buffer = aa_get_buffer();
+ buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
@@ -703,8 +703,8 @@ int aa_pivotroot(struct aa_label *label, const struct path *old_path,
AA_BUG(!old_path);
AA_BUG(!new_path);
- old_buffer = aa_get_buffer();
- new_buffer = aa_get_buffer();
+ old_buffer = aa_get_buffer(false);
+ new_buffer = aa_get_buffer(false);
error = -ENOMEM;
if (!old_buffer || !new_buffer)
goto out;