// SPDX-License-Identifier: GPL-2.0
#include "audit.h"
#include <linux/fsnotify_backend.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/kthread.h>
#include <linux/refcount.h>
#include <linux/slab.h>
struct audit_tree;
struct audit_chunk;
struct audit_tree {
refcount_t count;
int goner;
struct audit_chunk *root;
struct list_head chunks;
struct list_head rules;
struct list_head list;
struct list_head same_root;
struct rcu_head head;
char pathname[];
};
struct audit_chunk {
struct list_head hash;
unsigned long key;
struct fsnotify_mark *mark;
struct list_head trees; /* with root here */
int count;
atomic_long_t refs;
struct rcu_head head;
struct node {
struct list_head list;
struct audit_tree *owner;
unsigned index; /* index; upper bit indicates 'will prune' */
} owners[];
};
struct audit_tree_mark {
struct fsnotify_mark mark;
struct audit_chunk *chunk;
};
static LIST_HEAD(tree_list);
static LIST_HEAD(prune_list);
static struct task_struct *prune_thread;
/*
* One struct chunk is attached to each inode of interest through
* audit_tree_mark (fsnotify mark). We replace struct chunk on tagging /
* untagging, the mark is stable as long as there is chunk attached. The
* association between mark and chunk is protected by hash_lock and
* audit_tree_group->mark_mutex. Thus as long as we hold
* audit_tree_group->mark_mutex and check that the mark is alive by
* FSNOTIFY_MARK_FLAG_ATTACHED flag check, we are sure the mark points to
* the current chunk.
*
* Rules have pointer to struct audit_tree.
* Rules have struct list_head rlist forming a list of rules over
* the same tree.
* References to struct chunk are collected at audit_inode{,_child}()
* time and used in AUDIT_TREE rule matching.
* These references are dropped at the same time we are calling
* audit_free_names(), etc.
*
* Cyclic lists galore:
* tree.chunks anchors chunk.owners[].list hash_lock
* tree.rules anchors rule.rlist audit_filter_mutex
* chunk.trees anchors tree.same_root hash_lock
* chunk.hash is a hash with middle bits of watch.inode as
* a hash function. RCU, hash_lock
*
* tree is refcounted; one reference for "some rules on rules_list refer to
* it", one for each chunk with pointer to it.
*
* chunk is refcounted by embedded .refs. Mark associated with the chunk holds
* one chunk reference. This reference is dropped either when a mark is going
* to be freed (corresponding inode goes away) or when chunk attached to the
* mark gets replaced. This reference must be dropped using
* audit_mark_put_chunk() to make sure the reference is dropped only after RCU
* grace period as it protects RCU readers of the hash table.
*
* node.index allows to get from node.list to containing chunk.
* MSB of that sucker is stolen to mark taggings that we might have to
* revert - several operations have very unpleasant cleanup logics and
* that makes a difference. Some.
*/
static struct fsnotify_group *audit_tree_group;
static struct kmem_cache *audit_tree_mark_cachep __read_mostly;
static struct audit_tree *alloc_tree(const char *s)
{
struct audit_tree *tree;
tree = kmalloc(sizeof(struct audit_tree) + strlen(s) + 1, GFP_KERNEL);
if (tree) {
refcount_set(&tree->count, 1);
tree->goner = 0;
INIT_LIST_HEAD(&tree->chunks);
INIT_LIST_HEAD(&tree->rules);
INIT_LIST_HEAD(&tree->list);
INIT_LIST_HEAD(&tree->same_root);
tree->root = NULL;
strcpy(tree->pathname, s);
}
return tree;
}
static inline void get_tree(struct audit_tree *tree)
{
refcount_inc(&tree->count);
}
static inline void put_tree(struct audit_tree *tree)
{
if (refcount_dec_and_test(&tree->count))
kfree_rcu(tree, head);
}
/* to avoid bringing the entire thing in audit.h */
const char *audit_tree_path(struct audit_tree *tree)
{
return tree->pathname;
}
static void free_chunk(struct audit_chunk *chunk)
{
int i;
for (i = 0; i < chunk->count; i++) {
if (chunk->owners[i].owner)
put_tree(chunk->owners[i].owner);
}
kfree(chunk);
}
void audit_put_chunk(struct audit_chunk *chunk)
{
if (atomic_long_dec_and_test(&chunk->refs))
free_chunk(chunk);
}
static void __put_chunk(struct rcu_head *rcu)
{
struct audit_chunk *chunk = container_of(rcu, struct audit_chunk, head);
audit_put_chunk(chunk);
}
/*
* Drop reference to the chunk that was held by the mark. This is the reference
* that gets dropped after we've removed the chunk from the hash table and we
* use it to make sure chunk cannot be freed before RCU grace period expires.
*/
static void audit_mark_put_chunk(struct audit_chunk *chunk)
{
call_rcu(&chunk->head, __put_chunk);
}
static inline struct audit_tree_mark *audit_mark(struct fsnotify_mark *mark)
{
return container_of(mark, struct audit_tree_mark, mark);
}
static struct audit_chunk *mark_chunk(struct fsnotify_mark *mark)
{
return audit_mark(mark)->chunk;
}
static void audit_tree_destroy_watch(struct fsnotify_mark *mark)
{
kmem_cache_free(audit_tree_mark_cachep, audit_mark(mark));
}
static struct fsnotify_mark *alloc_mark(void)
{
struct audit_tree_mark *amark;
amark = kmem_cache_zalloc(audit_tree_mark_cachep, GFP_KERNEL);
if (!amark)
return NULL;
fsnotify_init_mark(&amark->mark, audit_tree_group);
amark->mark.mask = FS_IN_IGNORED;
return &