cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

inotify_fsnotify.c (5827B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * fs/inotify_user.c - inotify support for userspace
      4 *
      5 * Authors:
      6 *	John McCutchan	<ttb@tentacle.dhs.org>
      7 *	Robert Love	<rml@novell.com>
      8 *
      9 * Copyright (C) 2005 John McCutchan
     10 * Copyright 2006 Hewlett-Packard Development Company, L.P.
     11 *
     12 * Copyright (C) 2009 Eric Paris <Red Hat Inc>
     13 * inotify was largely rewriten to make use of the fsnotify infrastructure
     14 */
     15
     16#include <linux/dcache.h> /* d_unlinked */
     17#include <linux/fs.h> /* struct inode */
     18#include <linux/fsnotify_backend.h>
     19#include <linux/inotify.h>
     20#include <linux/path.h> /* struct path */
     21#include <linux/slab.h> /* kmem_* */
     22#include <linux/types.h>
     23#include <linux/sched.h>
     24#include <linux/sched/user.h>
     25#include <linux/sched/mm.h>
     26
     27#include "inotify.h"
     28
     29/*
     30 * Check if 2 events contain the same information.
     31 */
     32static bool event_compare(struct fsnotify_event *old_fsn,
     33			  struct fsnotify_event *new_fsn)
     34{
     35	struct inotify_event_info *old, *new;
     36
     37	old = INOTIFY_E(old_fsn);
     38	new = INOTIFY_E(new_fsn);
     39	if (old->mask & FS_IN_IGNORED)
     40		return false;
     41	if ((old->mask == new->mask) &&
     42	    (old->wd == new->wd) &&
     43	    (old->name_len == new->name_len) &&
     44	    (!old->name_len || !strcmp(old->name, new->name)))
     45		return true;
     46	return false;
     47}
     48
     49static int inotify_merge(struct fsnotify_group *group,
     50			 struct fsnotify_event *event)
     51{
     52	struct list_head *list = &group->notification_list;
     53	struct fsnotify_event *last_event;
     54
     55	last_event = list_entry(list->prev, struct fsnotify_event, list);
     56	return event_compare(last_event, event);
     57}
     58
     59int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask,
     60			       struct inode *inode, struct inode *dir,
     61			       const struct qstr *name, u32 cookie)
     62{
     63	struct inotify_inode_mark *i_mark;
     64	struct inotify_event_info *event;
     65	struct fsnotify_event *fsn_event;
     66	struct fsnotify_group *group = inode_mark->group;
     67	int ret;
     68	int len = 0;
     69	int alloc_len = sizeof(struct inotify_event_info);
     70	struct mem_cgroup *old_memcg;
     71
     72	if (name) {
     73		len = name->len;
     74		alloc_len += len + 1;
     75	}
     76
     77	pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark,
     78		 mask);
     79
     80	i_mark = container_of(inode_mark, struct inotify_inode_mark,
     81			      fsn_mark);
     82
     83	/*
     84	 * Whoever is interested in the event, pays for the allocation. Do not
     85	 * trigger OOM killer in the target monitoring memcg as it may have
     86	 * security repercussion.
     87	 */
     88	old_memcg = set_active_memcg(group->memcg);
     89	event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
     90	set_active_memcg(old_memcg);
     91
     92	if (unlikely(!event)) {
     93		/*
     94		 * Treat lost event due to ENOMEM the same way as queue
     95		 * overflow to let userspace know event was lost.
     96		 */
     97		fsnotify_queue_overflow(group);
     98		return -ENOMEM;
     99	}
    100
    101	/*
    102	 * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events
    103	 * for fanotify. inotify never reported IN_ISDIR with those events.
    104	 * It looks like an oversight, but to avoid the risk of breaking
    105	 * existing inotify programs, mask the flag out from those events.
    106	 */
    107	if (mask & (IN_MOVE_SELF | IN_DELETE_SELF))
    108		mask &= ~IN_ISDIR;
    109
    110	fsn_event = &event->fse;
    111	fsnotify_init_event(fsn_event);
    112	event->mask = mask;
    113	event->wd = i_mark->wd;
    114	event->sync_cookie = cookie;
    115	event->name_len = len;
    116	if (len)
    117		strcpy(event->name, name->name);
    118
    119	ret = fsnotify_add_event(group, fsn_event, inotify_merge);
    120	if (ret) {
    121		/* Our event wasn't used in the end. Free it. */
    122		fsnotify_destroy_event(group, fsn_event);
    123	}
    124
    125	if (inode_mark->flags & FSNOTIFY_MARK_FLAG_IN_ONESHOT)
    126		fsnotify_destroy_mark(inode_mark, group);
    127
    128	return 0;
    129}
    130
    131static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group)
    132{
    133	inotify_ignored_and_remove_idr(fsn_mark, group);
    134}
    135
    136/*
    137 * This is NEVER supposed to be called.  Inotify marks should either have been
    138 * removed from the idr when the watch was removed or in the
    139 * fsnotify_destroy_mark_by_group() call when the inotify instance was being
    140 * torn down.  This is only called if the idr is about to be freed but there
    141 * are still marks in it.
    142 */
    143static int idr_callback(int id, void *p, void *data)
    144{
    145	struct fsnotify_mark *fsn_mark;
    146	struct inotify_inode_mark *i_mark;
    147	static bool warned = false;
    148
    149	if (warned)
    150		return 0;
    151
    152	warned = true;
    153	fsn_mark = p;
    154	i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
    155
    156	WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in "
    157		"idr.  Probably leaking memory\n", id, p, data);
    158
    159	/*
    160	 * I'm taking the liberty of assuming that the mark in question is a
    161	 * valid address and I'm dereferencing it.  This might help to figure
    162	 * out why we got here and the panic is no worse than the original
    163	 * BUG() that was here.
    164	 */
    165	if (fsn_mark)
    166		printk(KERN_WARNING "fsn_mark->group=%p wd=%d\n",
    167			fsn_mark->group, i_mark->wd);
    168	return 0;
    169}
    170
    171static void inotify_free_group_priv(struct fsnotify_group *group)
    172{
    173	/* ideally the idr is empty and we won't hit the BUG in the callback */
    174	idr_for_each(&group->inotify_data.idr, idr_callback, group);
    175	idr_destroy(&group->inotify_data.idr);
    176	if (group->inotify_data.ucounts)
    177		dec_inotify_instances(group->inotify_data.ucounts);
    178}
    179
    180static void inotify_free_event(struct fsnotify_group *group,
    181			       struct fsnotify_event *fsn_event)
    182{
    183	kfree(INOTIFY_E(fsn_event));
    184}
    185
    186/* ding dong the mark is dead */
    187static void inotify_free_mark(struct fsnotify_mark *fsn_mark)
    188{
    189	struct inotify_inode_mark *i_mark;
    190
    191	i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
    192
    193	kmem_cache_free(inotify_inode_mark_cachep, i_mark);
    194}
    195
    196const struct fsnotify_ops inotify_fsnotify_ops = {
    197	.handle_inode_event = inotify_handle_inode_event,
    198	.free_group_priv = inotify_free_group_priv,
    199	.free_event = inotify_free_event,
    200	.freeing_mark = inotify_freeing_mark,
    201	.free_mark = inotify_free_mark,
    202};