Merge branch 'upstream' of git://git.infradead.org/users/pcmoore/audit
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 8 Sep 2015 20:34:59 +0000 (13:34 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 8 Sep 2015 20:34:59 +0000 (13:34 -0700)
Pull audit update from Paul Moore:
 "This is one of the larger audit patchsets in recent history,
  consisting of eight patches and almost 400 lines of changes.

  The bulk of the patchset is the new "audit by executable"
  functionality which allows admins to set an audit watch based on the
  executable on disk.  Prior to this, admins could only track an
  application by PID, which has some obvious limitations.

  Beyond the new functionality we also have some refcnt fixes and a few
  minor cleanups"

* 'upstream' of git://git.infradead.org/users/pcmoore/audit:
  fixup: audit: implement audit by executable
  audit: implement audit by executable
  audit: clean simple fsnotify implementation
  audit: use macros for unset inode and device values
  audit: make audit_del_rule() more robust
  audit: fix uninitialized variable in audit_add_rule()
  audit: eliminate unnecessary extra layer of watch parent references
  audit: eliminate unnecessary extra layer of watch references

1  2 
include/uapi/linux/audit.h
kernel/Makefile
kernel/audit.c
kernel/audit.h
kernel/audit_tree.c
kernel/audit_watch.c
kernel/auditsc.c

  #define AUDIT_OBJ_UID 109
  #define AUDIT_OBJ_GID 110
  #define AUDIT_FIELD_COMPARE   111
+ #define AUDIT_EXE     112
  
  #define AUDIT_ARG0      200
  #define AUDIT_ARG1      (AUDIT_ARG0+1)
@@@ -324,8 -325,10 +325,10 @@@ enum 
  
  #define AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT    0x00000001
  #define AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME        0x00000002
+ #define AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH  0x00000004
  #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \
-                                 AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME)
+                                 AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \
+                                 AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH)
  
  /* deprecated: AUDIT_VERSION_* */
  #define AUDIT_VERSION_LATEST          AUDIT_FEATURE_BITMAP_ALL
  #define AUDIT_ARCH_SHEL64     (EM_SH|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
  #define AUDIT_ARCH_SPARC      (EM_SPARC)
  #define AUDIT_ARCH_SPARC64    (EM_SPARCV9|__AUDIT_ARCH_64BIT)
 +#define AUDIT_ARCH_TILEGX     (EM_TILEGX|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
 +#define AUDIT_ARCH_TILEGX32   (EM_TILEGX|__AUDIT_ARCH_LE)
 +#define AUDIT_ARCH_TILEPRO    (EM_TILEPRO|__AUDIT_ARCH_LE)
  #define AUDIT_ARCH_X86_64     (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
  
  #define AUDIT_PERM_EXEC               1
diff --combined kernel/Makefile
@@@ -9,9 -9,7 +9,9 @@@ obj-y     = fork.o exec_domain.o panic.
            extable.o params.o \
            kthread.o sys_ni.o nsproxy.o \
            notifier.o ksysfs.o cred.o reboot.o \
 -          async.o range.o groups.o smpboot.o
 +          async.o range.o smpboot.o
 +
 +obj-$(CONFIG_MULTIUSER) += groups.o
  
  ifdef CONFIG_FUNCTION_TRACER
  # Do not trace debug files and internal ftrace files
@@@ -45,6 -43,7 +45,6 @@@ ifneq ($(CONFIG_SMP),y
  obj-y += up.o
  endif
  obj-$(CONFIG_UID16) += uid16.o
 -obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
  obj-$(CONFIG_MODULES) += module.o
  obj-$(CONFIG_MODULE_SIG) += module_signing.o
  obj-$(CONFIG_KALLSYMS) += kallsyms.o
@@@ -54,7 -53,6 +54,7 @@@ obj-$(CONFIG_BACKTRACE_SELF_TEST) += ba
  obj-$(CONFIG_COMPAT) += compat.o
  obj-$(CONFIG_CGROUPS) += cgroup.o
  obj-$(CONFIG_CGROUP_FREEZER) += cgroup_freezer.o
 +obj-$(CONFIG_CGROUP_PIDS) += cgroup_pids.o
  obj-$(CONFIG_CPUSETS) += cpuset.o
  obj-$(CONFIG_UTS_NS) += utsname.o
  obj-$(CONFIG_USER_NS) += user_namespace.o
@@@ -64,7 -62,7 +64,7 @@@ obj-$(CONFIG_SMP) += stop_machine.
  obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
  obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
  obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
- obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o
+ obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o audit_fsnotify.o
  obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
  obj-$(CONFIG_GCOV_KERNEL) += gcov/
  obj-$(CONFIG_KPROBES) += kprobes.o
@@@ -111,3 -109,99 +111,3 @@@ $(obj)/config_data.gz: $(KCONFIG_CONFIG
  targets += config_data.h
  $(obj)/config_data.h: $(obj)/config_data.gz FORCE
        $(call filechk,ikconfiggz)
 -
 -###############################################################################
 -#
 -# Roll all the X.509 certificates that we can find together and pull them into
 -# the kernel so that they get loaded into the system trusted keyring during
 -# boot.
 -#
 -# We look in the source root and the build root for all files whose name ends
 -# in ".x509".  Unfortunately, this will generate duplicate filenames, so we
 -# have make canonicalise the pathnames and then sort them to discard the
 -# duplicates.
 -#
 -###############################################################################
 -ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
 -X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509)
 -X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += $(objtree)/signing_key.x509
 -X509_CERTIFICATES-raw := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \
 -                              $(or $(realpath $(CERT)),$(CERT))))
 -X509_CERTIFICATES := $(subst $(realpath $(objtree))/,,$(X509_CERTIFICATES-raw))
 -
 -ifeq ($(X509_CERTIFICATES),)
 -$(warning *** No X.509 certificates found ***)
 -endif
 -
 -ifneq ($(wildcard $(obj)/.x509.list),)
 -ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
 -$(info X.509 certificate list changed)
 -$(shell rm $(obj)/.x509.list)
 -endif
 -endif
 -
 -kernel/system_certificates.o: $(obj)/x509_certificate_list
 -
 -quiet_cmd_x509certs  = CERTS   $@
 -      cmd_x509certs  = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; $(kecho) "  - Including cert $(X509)")
 -
 -targets += $(obj)/x509_certificate_list
 -$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list
 -      $(call if_changed,x509certs)
 -
 -targets += $(obj)/.x509.list
 -$(obj)/.x509.list:
 -      @echo $(X509_CERTIFICATES) >$@
 -endif
 -
 -clean-files := x509_certificate_list .x509.list
 -
 -ifeq ($(CONFIG_MODULE_SIG),y)
 -###############################################################################
 -#
 -# If module signing is requested, say by allyesconfig, but a key has not been
 -# supplied, then one will need to be generated to make sure the build does not
 -# fail and that the kernel may be used afterwards.
 -#
 -###############################################################################
 -ifndef CONFIG_MODULE_SIG_HASH
 -$(error Could not determine digest type to use from kernel config)
 -endif
 -
 -signing_key.priv signing_key.x509: x509.genkey
 -      @echo "###"
 -      @echo "### Now generating an X.509 key pair to be used for signing modules."
 -      @echo "###"
 -      @echo "### If this takes a long time, you might wish to run rngd in the"
 -      @echo "### background to keep the supply of entropy topped up.  It"
 -      @echo "### needs to be run as root, and uses a hardware random"
 -      @echo "### number generator if one is available."
 -      @echo "###"
 -      openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
 -              -batch -x509 -config x509.genkey \
 -              -outform DER -out signing_key.x509 \
 -              -keyout signing_key.priv 2>&1
 -      @echo "###"
 -      @echo "### Key pair generated."
 -      @echo "###"
 -
 -x509.genkey:
 -      @echo Generating X.509 key generation config
 -      @echo  >x509.genkey "[ req ]"
 -      @echo >>x509.genkey "default_bits = 4096"
 -      @echo >>x509.genkey "distinguished_name = req_distinguished_name"
 -      @echo >>x509.genkey "prompt = no"
 -      @echo >>x509.genkey "string_mask = utf8only"
 -      @echo >>x509.genkey "x509_extensions = myexts"
 -      @echo >>x509.genkey
 -      @echo >>x509.genkey "[ req_distinguished_name ]"
 -      @echo >>x509.genkey "O = Magrathea"
 -      @echo >>x509.genkey "CN = Glacier signing key"
 -      @echo >>x509.genkey "emailAddress = slartibartfast@magrathea.h2g2"
 -      @echo >>x509.genkey
 -      @echo >>x509.genkey "[ myexts ]"
 -      @echo >>x509.genkey "basicConstraints=critical,CA:FALSE"
 -      @echo >>x509.genkey "keyUsage=digitalSignature"
 -      @echo >>x509.genkey "subjectKeyIdentifier=hash"
 -      @echo >>x509.genkey "authorityKeyIdentifier=keyid"
 -endif
diff --combined kernel/audit.c
@@@ -43,7 -43,6 +43,7 @@@
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
 +#include <linux/file.h>
  #include <linux/init.h>
  #include <linux/types.h>
  #include <linux/atomic.h>
@@@ -108,7 -107,6 +108,7 @@@ static u32 audit_rate_limit
   * When set to zero, this means unlimited. */
  static u32    audit_backlog_limit = 64;
  #define AUDIT_BACKLOG_WAIT_TIME (60 * HZ)
 +static u32    audit_backlog_wait_time_master = AUDIT_BACKLOG_WAIT_TIME;
  static u32    audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
  static u32    audit_backlog_wait_overflow = 0;
  
@@@ -340,13 -338,13 +340,13 @@@ static int audit_set_backlog_limit(u32 
  static int audit_set_backlog_wait_time(u32 timeout)
  {
        return audit_do_config_change("audit_backlog_wait_time",
 -                                    &audit_backlog_wait_time, timeout);
 +                                    &audit_backlog_wait_time_master, timeout);
  }
  
  static int audit_set_enabled(u32 state)
  {
        int rc;
 -      if (state < AUDIT_OFF || state > AUDIT_LOCKED)
 +      if (state > AUDIT_LOCKED)
                return -EINVAL;
  
        rc =  audit_do_config_change("audit_enabled", &audit_enabled, state);
@@@ -665,7 -663,7 +665,7 @@@ static int audit_netlink_ok(struct sk_b
        case AUDIT_MAKE_EQUIV:
                /* Only support auditd and auditctl in initial pid namespace
                 * for now. */
 -              if ((task_active_pid_ns(current) != &init_pid_ns))
 +              if (task_active_pid_ns(current) != &init_pid_ns)
                        return -EPERM;
  
                if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
@@@ -836,7 -834,7 +836,7 @@@ static int audit_receive_msg(struct sk_
                s.lost                  = atomic_read(&audit_lost);
                s.backlog               = skb_queue_len(&audit_skb_queue);
                s.feature_bitmap        = AUDIT_FEATURE_BITMAP_ALL;
 -              s.backlog_wait_time     = audit_backlog_wait_time;
 +              s.backlog_wait_time     = audit_backlog_wait_time_master;
                audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &s, sizeof(s));
                break;
        }
                if (s.mask & AUDIT_STATUS_BACKLOG_WAIT_TIME) {
                        if (sizeof(s) > (size_t)nlh->nlmsg_len)
                                return -EINVAL;
 -                      if (s.backlog_wait_time < 0 ||
 -                          s.backlog_wait_time > 10*AUDIT_BACKLOG_WAIT_TIME)
 +                      if (s.backlog_wait_time > 10*AUDIT_BACKLOG_WAIT_TIME)
                                return -EINVAL;
                        err = audit_set_backlog_wait_time(s.backlog_wait_time);
                        if (err < 0)
@@@ -1386,8 -1385,7 +1386,8 @@@ struct audit_buffer *audit_log_start(st
                return NULL;
        }
  
 -      audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
 +      if (!reserve)
 +              audit_backlog_wait_time = audit_backlog_wait_time_master;
  
        ab = audit_buffer_alloc(ctx, gfp_mask, type);
        if (!ab) {
@@@ -1761,7 -1759,7 +1761,7 @@@ void audit_log_name(struct audit_contex
        } else
                audit_log_format(ab, " name=(null)");
  
-       if (n->ino != (unsigned long)-1)
 -      if (n->ino != AUDIT_INO_UNSET) {
++      if (n->ino != AUDIT_INO_UNSET)
                audit_log_format(ab, " inode=%lu"
                                 " dev=%02x:%02x mode=%#ho"
                                 " ouid=%u ogid=%u rdev=%02x:%02x",
                                 from_kgid(&init_user_ns, n->gid),
                                 MAJOR(n->rdev),
                                 MINOR(n->rdev));
 -      }
        if (n->osid != 0) {
                char *ctx = NULL;
                u32 len;
@@@ -1839,29 -1838,11 +1839,29 @@@ error_path
  }
  EXPORT_SYMBOL(audit_log_task_context);
  
 +void audit_log_d_path_exe(struct audit_buffer *ab,
 +                        struct mm_struct *mm)
 +{
 +      struct file *exe_file;
 +
 +      if (!mm)
 +              goto out_null;
 +
 +      exe_file = get_mm_exe_file(mm);
 +      if (!exe_file)
 +              goto out_null;
 +
 +      audit_log_d_path(ab, " exe=", &exe_file->f_path);
 +      fput(exe_file);
 +      return;
 +out_null:
 +      audit_log_format(ab, " exe=(null)");
 +}
 +
  void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
  {
        const struct cred *cred;
        char comm[sizeof(tsk->comm)];
 -      struct mm_struct *mm = tsk->mm;
        char *tty;
  
        if (!ab)
        audit_log_format(ab, " comm=");
        audit_log_untrustedstring(ab, get_task_comm(comm, tsk));
  
 -      if (mm) {
 -              down_read(&mm->mmap_sem);
 -              if (mm->exe_file)
 -                      audit_log_d_path(ab, " exe=", &mm->exe_file->f_path);
 -              up_read(&mm->mmap_sem);
 -      } else
 -              audit_log_format(ab, " exe=(null)");
 +      audit_log_d_path_exe(ab, tsk->mm);
        audit_log_task_context(ab);
  }
  EXPORT_SYMBOL(audit_log_task_info);
@@@ -1928,7 -1915,7 +1928,7 @@@ void audit_log_link_denied(const char *
  
        /* Generate AUDIT_PATH record with object. */
        name->type = AUDIT_TYPE_NORMAL;
 -      audit_copy_inode(name, link->dentry, link->dentry->d_inode);
 +      audit_copy_inode(name, link->dentry, d_backing_inode(link->dentry));
        audit_log_name(current->audit_context, name, link, 0, NULL);
  out:
        kfree(name);
diff --combined kernel/audit.h
@@@ -50,6 -50,7 +50,7 @@@ enum audit_state 
  
  /* Rule lists */
  struct audit_watch;
+ struct audit_fsnotify_mark;
  struct audit_tree;
  struct audit_chunk;
  
@@@ -252,14 -253,12 +253,15 @@@ struct audit_net 
  extern int selinux_audit_rule_update(void);
  
  extern struct mutex audit_filter_mutex;
+ extern int audit_del_rule(struct audit_entry *);
  extern void audit_free_rule_rcu(struct rcu_head *);
  extern struct list_head audit_filter_list[];
  
  extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
  
 +extern void audit_log_d_path_exe(struct audit_buffer *ab,
 +                               struct mm_struct *mm);
 +
  /* audit watch functions */
  #ifdef CONFIG_AUDIT_WATCH
  extern void audit_put_watch(struct audit_watch *watch);
@@@ -269,6 -268,15 +271,15 @@@ extern int audit_add_watch(struct audit
  extern void audit_remove_watch_rule(struct audit_krule *krule);
  extern char *audit_watch_path(struct audit_watch *watch);
  extern int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev);
+ extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len);
+ extern char *audit_mark_path(struct audit_fsnotify_mark *mark);
+ extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark);
+ extern void audit_remove_mark_rule(struct audit_krule *krule);
+ extern int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev);
+ extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old);
+ extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark);
  #else
  #define audit_put_watch(w) {}
  #define audit_get_watch(w) {}
  #define audit_watch_path(w) ""
  #define audit_watch_compare(w, i, d) 0
  
+ #define audit_alloc_mark(k, p, l) (ERR_PTR(-EINVAL))
+ #define audit_mark_path(m) ""
+ #define audit_remove_mark(m)
+ #define audit_remove_mark_rule(k)
+ #define audit_mark_compare(m, i, d) 0
+ #define audit_exe_compare(t, m) (-EINVAL)
+ #define audit_dupe_exe(n, o) (-EINVAL)
  #endif /* CONFIG_AUDIT_WATCH */
  
  #ifdef CONFIG_AUDIT_TREE
diff --combined kernel/audit_tree.c
@@@ -37,7 -37,6 +37,7 @@@ struct audit_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.
@@@ -479,6 -478,8 +479,8 @@@ static void kill_rules(struct audit_tre
                if (rule->tree) {
                        /* not a half-baked one */
                        audit_tree_log_remove_rule(rule);
+                       if (entry->rule.exe)
+                               audit_remove_mark(entry->rule.exe);
                        rule->tree = NULL;
                        list_del_rcu(&entry->list);
                        list_del(&entry->rule.list);
@@@ -577,7 -578,7 +579,7 @@@ int audit_remove_tree_rule(struct audit
  
  static int compare_root(struct vfsmount *mnt, void *arg)
  {
 -      return mnt->mnt_root->d_inode == arg;
 +      return d_backing_inode(mnt->mnt_root) == arg;
  }
  
  void audit_trim_trees(void)
@@@ -649,58 -650,7 +651,58 @@@ void audit_put_tree(struct audit_tree *
  
  static int tag_mount(struct vfsmount *mnt, void *arg)
  {
 -      return tag_chunk(mnt->mnt_root->d_inode, arg);
 +      return tag_chunk(d_backing_inode(mnt->mnt_root), arg);
 +}
 +
 +/*
 + * That gets run when evict_chunk() ends up needing to kill audit_tree.
 + * Runs from a separate thread.
 + */
 +static int prune_tree_thread(void *unused)
 +{
 +      for (;;) {
 +              set_current_state(TASK_INTERRUPTIBLE);
 +              if (list_empty(&prune_list))
 +                      schedule();
 +              __set_current_state(TASK_RUNNING);
 +
 +              mutex_lock(&audit_cmd_mutex);
 +              mutex_lock(&audit_filter_mutex);
 +
 +              while (!list_empty(&prune_list)) {
 +                      struct audit_tree *victim;
 +
 +                      victim = list_entry(prune_list.next,
 +                                      struct audit_tree, list);
 +                      list_del_init(&victim->list);
 +
 +                      mutex_unlock(&audit_filter_mutex);
 +
 +                      prune_one(victim);
 +
 +                      mutex_lock(&audit_filter_mutex);
 +              }
 +
 +              mutex_unlock(&audit_filter_mutex);
 +              mutex_unlock(&audit_cmd_mutex);
 +      }
 +      return 0;
 +}
 +
 +static int audit_launch_prune(void)
 +{
 +      if (prune_thread)
 +              return 0;
 +      prune_thread = kthread_create(prune_tree_thread, NULL,
 +                              "audit_prune_tree");
 +      if (IS_ERR(prune_thread)) {
 +              pr_err("cannot start thread audit_prune_tree");
 +              prune_thread = NULL;
 +              return -ENOMEM;
 +      } else {
 +              wake_up_process(prune_thread);
 +              return 0;
 +      }
  }
  
  /* called with audit_filter_mutex */
@@@ -726,12 -676,6 +728,12 @@@ int audit_add_tree_rule(struct audit_kr
        /* do not set rule->tree yet */
        mutex_unlock(&audit_filter_mutex);
  
 +      if (unlikely(!prune_thread)) {
 +              err = audit_launch_prune();
 +              if (err)
 +                      goto Err;
 +      }
 +
        err = kern_path(tree->pathname, 0, &path);
        if (err)
                goto Err;
@@@ -869,10 -813,36 +871,10 @@@ int audit_tag_tree(char *old, char *new
        return failed;
  }
  
 -/*
 - * That gets run when evict_chunk() ends up needing to kill audit_tree.
 - * Runs from a separate thread.
 - */
 -static int prune_tree_thread(void *unused)
 -{
 -      mutex_lock(&audit_cmd_mutex);
 -      mutex_lock(&audit_filter_mutex);
 -
 -      while (!list_empty(&prune_list)) {
 -              struct audit_tree *victim;
 -
 -              victim = list_entry(prune_list.next, struct audit_tree, list);
 -              list_del_init(&victim->list);
 -
 -              mutex_unlock(&audit_filter_mutex);
 -
 -              prune_one(victim);
 -
 -              mutex_lock(&audit_filter_mutex);
 -      }
 -
 -      mutex_unlock(&audit_filter_mutex);
 -      mutex_unlock(&audit_cmd_mutex);
 -      return 0;
 -}
  
  static void audit_schedule_prune(void)
  {
 -      kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
 +      wake_up_process(prune_thread);
  }
  
  /*
@@@ -939,9 -909,9 +941,9 @@@ static void evict_chunk(struct audit_ch
        for (n = 0; n < chunk->count; n++)
                list_del_init(&chunk->owners[n].list);
        spin_unlock(&hash_lock);
 +      mutex_unlock(&audit_filter_mutex);
        if (need_prune)
                audit_schedule_prune();
 -      mutex_unlock(&audit_filter_mutex);
  }
  
  static int audit_tree_handle_event(struct fsnotify_group *group,
diff --combined kernel/audit_watch.c
@@@ -138,7 -138,7 +138,7 @@@ char *audit_watch_path(struct audit_wat
  
  int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
  {
-       return (watch->ino != (unsigned long)-1) &&
+       return (watch->ino != AUDIT_INO_UNSET) &&
                (watch->ino == ino) &&
                (watch->dev == dev);
  }
  /* Initialize a parent watch entry. */
  static struct audit_parent *audit_init_parent(struct path *path)
  {
 -      struct inode *inode = path->dentry->d_inode;
 +      struct inode *inode = d_backing_inode(path->dentry);
        struct audit_parent *parent;
        int ret;
  
@@@ -179,8 -179,8 +179,8 @@@ static struct audit_watch *audit_init_w
        INIT_LIST_HEAD(&watch->rules);
        atomic_set(&watch->count, 1);
        watch->path = path;
-       watch->dev = (dev_t)-1;
-       watch->ino = (unsigned long)-1;
+       watch->dev = AUDIT_DEV_UNSET;
+       watch->ino = AUDIT_INO_UNSET;
  
        return watch;
  }
@@@ -203,7 -203,6 +203,6 @@@ int audit_to_watch(struct audit_krule *
        if (IS_ERR(watch))
                return PTR_ERR(watch);
  
-       audit_get_watch(watch);
        krule->watch = watch;
  
        return 0;
@@@ -313,6 -312,8 +312,8 @@@ static void audit_update_watch(struct a
                                list_replace(&oentry->rule.list,
                                             &nentry->rule.list);
                        }
+                       if (oentry->rule.exe)
+                               audit_remove_mark(oentry->rule.exe);
  
                        audit_watch_log_rule_change(r, owatch, "updated_rules");
  
@@@ -343,6 -344,8 +344,8 @@@ static void audit_remove_parent_watches
                list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
                        e = container_of(r, struct audit_entry, rule);
                        audit_watch_log_rule_change(r, w, "remove_rule");
+                       if (e->rule.exe)
+                               audit_remove_mark(e->rule.exe);
                        list_del(&r->rlist);
                        list_del(&r->list);
                        list_del_rcu(&e->list);
@@@ -361,11 -364,11 +364,11 @@@ static int audit_get_nd(struct audit_wa
        struct dentry *d = kern_path_locked(watch->path, parent);
        if (IS_ERR(d))
                return PTR_ERR(d);
 -      mutex_unlock(&parent->dentry->d_inode->i_mutex);
 -      if (d->d_inode) {
 +      mutex_unlock(&d_backing_inode(parent->dentry)->i_mutex);
 +      if (d_is_positive(d)) {
                /* update watch filter fields */
 -              watch->dev = d->d_inode->i_sb->s_dev;
 -              watch->ino = d->d_inode->i_ino;
 +              watch->dev = d_backing_inode(d)->i_sb->s_dev;
 +              watch->ino = d_backing_inode(d)->i_ino;
        }
        dput(d);
        return 0;
@@@ -387,19 -390,20 +390,20 @@@ static void audit_add_to_parent(struct 
  
                watch_found = 1;
  
-               /* put krule's and initial refs to temporary watch */
-               audit_put_watch(watch);
+               /* put krule's ref to temporary watch */
                audit_put_watch(watch);
  
                audit_get_watch(w);
                krule->watch = watch = w;
+               audit_put_parent(parent);
                break;
        }
  
        if (!watch_found) {
-               audit_get_parent(parent);
                watch->parent = parent;
  
+               audit_get_watch(watch);
                list_add(&watch->wlist, &parent->watches);
        }
        list_add(&krule->rlist, &watch->rules);
@@@ -426,7 -430,7 +430,7 @@@ int audit_add_watch(struct audit_krule 
                return ret;
  
        /* either find an old parent or attach a new one */
 -      parent = audit_find_parent(parent_path.dentry->d_inode);
 +      parent = audit_find_parent(d_backing_inode(parent_path.dentry));
        if (!parent) {
                parent = audit_init_parent(&parent_path);
                if (IS_ERR(parent)) {
  
        audit_add_to_parent(krule, parent);
  
-       /* match get in audit_find_parent or audit_init_parent */
-       audit_put_parent(parent);
        h = audit_hash_ino((u32)watch->ino);
        *list = &audit_inode_hash[h];
  error:
@@@ -482,7 -483,7 +483,7 @@@ static int audit_watch_handle_event(str
  
        switch (data_type) {
        case (FSNOTIFY_EVENT_PATH):
 -              inode = ((struct path *)data)->dentry->d_inode;
 +              inode = d_backing_inode(((struct path *)data)->dentry);
                break;
        case (FSNOTIFY_EVENT_INODE):
                inode = (struct inode *)data;
        if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
                audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
        else if (mask & (FS_DELETE|FS_MOVED_FROM))
-               audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
+               audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
        else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
                audit_remove_parent_watches(parent);
  
@@@ -517,3 -518,36 +518,36 @@@ static int __init audit_watch_init(void
        return 0;
  }
  device_initcall(audit_watch_init);
+ int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
+ {
+       struct audit_fsnotify_mark *audit_mark;
+       char *pathname;
+       pathname = kstrdup(audit_mark_path(old->exe), GFP_KERNEL);
+       if (!pathname)
+               return -ENOMEM;
+       audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
+       if (IS_ERR(audit_mark)) {
+               kfree(pathname);
+               return PTR_ERR(audit_mark);
+       }
+       new->exe = audit_mark;
+       return 0;
+ }
+ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
+ {
+       struct file *exe_file;
+       unsigned long ino;
+       dev_t dev;
+       rcu_read_lock();
+       exe_file = rcu_dereference(tsk->mm->exe_file);
+       ino = exe_file->f_inode->i_ino;
+       dev = exe_file->f_inode->i_sb->s_dev;
+       rcu_read_unlock();
+       return audit_mark_compare(mark, ino, dev);
+ }
diff --combined kernel/auditsc.c
@@@ -180,7 -180,7 +180,7 @@@ static int audit_match_filetype(struct 
                return 0;
  
        list_for_each_entry(n, &ctx->names_list, list) {
-               if ((n->ino != -1) &&
+               if ((n->ino != AUDIT_INO_UNSET) &&
                    ((n->mode & S_IFMT) == mode))
                        return 1;
        }
@@@ -466,6 -466,9 +466,9 @@@ static int audit_filter_rules(struct ta
                                result = audit_comparator(ctx->ppid, f->op, f->val);
                        }
                        break;
+               case AUDIT_EXE:
+                       result = audit_exe_compare(tsk, rule->exe);
+                       break;
                case AUDIT_UID:
                        result = audit_uid_comparator(cred->uid, f->op, f->uid);
                        break;
@@@ -1021,7 -1024,8 +1024,7 @@@ static int audit_log_single_execve_arg(
         * for strings that are too long, we should not have created
         * any.
         */
 -      if (unlikely((len == 0) || len > MAX_ARG_STRLEN - 1)) {
 -              WARN_ON(1);
 +      if (WARN_ON_ONCE(len < 0 || len > MAX_ARG_STRLEN - 1)) {
                send_sig(SIGKILL, current, 0);
                return -1;
        }
@@@ -1626,7 -1630,7 +1629,7 @@@ retry
        rcu_read_lock();
        seq = read_seqbegin(&rename_lock);
        for(;;) {
 -              struct inode *inode = d->d_inode;
 +              struct inode *inode = d_backing_inode(d);
                if (inode && unlikely(!hlist_empty(&inode->i_fsnotify_marks))) {
                        struct audit_chunk *chunk;
                        chunk = audit_tree_lookup(inode);
@@@ -1680,7 -1684,7 +1683,7 @@@ static struct audit_names *audit_alloc_
                aname->should_free = true;
        }
  
-       aname->ino = (unsigned long)-1;
+       aname->ino = AUDIT_INO_UNSET;
        aname->type = type;
        list_add_tail(&aname->list, &context->names_list);
  
@@@ -1751,7 -1755,7 +1754,7 @@@ void __audit_inode(struct filename *nam
                   unsigned int flags)
  {
        struct audit_context *context = current->audit_context;
 -      const struct inode *inode = dentry->d_inode;
 +      const struct inode *inode = d_backing_inode(dentry);
        struct audit_names *n;
        bool parent = flags & AUDIT_INODE_PARENT;
  
@@@ -1850,7 -1854,7 +1853,7 @@@ void __audit_inode_child(const struct i
                         const unsigned char type)
  {
        struct audit_context *context = current->audit_context;
 -      const struct inode *inode = dentry->d_inode;
 +      const struct inode *inode = d_backing_inode(dentry);
        const char *dname = dentry->d_name.name;
        struct audit_names *n, *found_parent = NULL, *found_child = NULL;
  
        if (inode)
                audit_copy_inode(found_child, dentry, inode);
        else
-               found_child->ino = (unsigned long)-1;
+               found_child->ino = AUDIT_INO_UNSET;
  }
  EXPORT_SYMBOL_GPL(__audit_inode_child);
  
@@@ -2358,6 -2362,7 +2361,6 @@@ static void audit_log_task(struct audit
        kuid_t auid, uid;
        kgid_t gid;
        unsigned int sessionid;
 -      struct mm_struct *mm = current->mm;
        char comm[sizeof(current->comm)];
  
        auid = audit_get_loginuid(current);
        audit_log_task_context(ab);
        audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
        audit_log_untrustedstring(ab, get_task_comm(comm, current));
 -      if (mm) {
 -              down_read(&mm->mmap_sem);
 -              if (mm->exe_file)
 -                      audit_log_d_path(ab, " exe=", &mm->exe_file->f_path);
 -              up_read(&mm->mmap_sem);
 -      } else
 -              audit_log_format(ab, " exe=(null)");
 +      audit_log_d_path_exe(ab, current->mm);
  }
  
  /**