]> Git Repo - J-linux.git/commitdiff
Merge tag 'selinux-pr-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <[email protected]>
Tue, 27 Apr 2021 20:42:11 +0000 (13:42 -0700)
committerLinus Torvalds <[email protected]>
Tue, 27 Apr 2021 20:42:11 +0000 (13:42 -0700)
Pull selinux updates from Paul Moore:

 - Add support for measuring the SELinux state and policy capabilities
   using IMA.

 - A handful of SELinux/NFS patches to compare the SELinux state of one
   mount with a set of mount options. Olga goes into more detail in the
   patch descriptions, but this is important as it allows more
   flexibility when using NFS and SELinux context mounts.

 - Properly differentiate between the subjective and objective LSM
   credentials; including support for the SELinux and Smack. My clumsy
   attempt at a proper fix for AppArmor didn't quite pass muster so John
   is working on a proper AppArmor patch, in the meantime this set of
   patches shouldn't change the behavior of AppArmor in any way. This
   change explains the bulk of the diffstat beyond security/.

 - Fix a problem where we were not properly terminating the permission
   list for two SELinux object classes.

* tag 'selinux-pr-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: add proper NULL termination to the secclass_map permissions
  smack: differentiate between subjective and objective task credentials
  selinux: clarify task subjective and objective credentials
  lsm: separate security_task_getsecid() into subjective and objective variants
  nfs: account for selinux security context when deciding to share superblock
  nfs: remove unneeded null check in nfs_fill_super()
  lsm,selinux: add new hook to compare new mount to an existing mount
  selinux: fix misspellings using codespell tool
  selinux: fix misspellings using codespell tool
  selinux: measure state and policy capabilities
  selinux: Allow context mounts for unpriviliged overlayfs

1  2 
drivers/android/binder.c
fs/nfs/internal.h
security/selinux/include/security.h
security/selinux/selinuxfs.c
security/selinux/ss/services.c

diff --combined drivers/android/binder.c
index 63d2c4339689730d5b4b80495116271ebf653da9,61d235b6ccd82176016bc1a58f731f77ccbfd43d..61d34e1dc59c5c6ff68924a5f4e066a4d66c4a98
@@@ -1506,12 -1506,6 +1506,12 @@@ static void binder_free_transaction(str
  
        if (target_proc) {
                binder_inner_proc_lock(target_proc);
 +              target_proc->outstanding_txns--;
 +              if (target_proc->outstanding_txns < 0)
 +                      pr_warn("%s: Unexpected outstanding_txns %d\n",
 +                              __func__, target_proc->outstanding_txns);
 +              if (!target_proc->outstanding_txns && target_proc->is_frozen)
 +                      wake_up_interruptible_all(&target_proc->freeze_wait);
                if (t->buffer)
                        t->buffer->transaction = NULL;
                binder_inner_proc_unlock(target_proc);
@@@ -2337,11 -2331,10 +2337,11 @@@ static int binder_fixup_parent(struct b
   * If the @thread parameter is not NULL, the transaction is always queued
   * to the waitlist of that specific thread.
   *
 - * Return:    true if the transactions was successfully queued
 - *            false if the target process or thread is dead
 + * Return:    0 if the transaction was successfully queued
 + *            BR_DEAD_REPLY if the target process or thread is dead
 + *            BR_FROZEN_REPLY if the target process or thread is frozen
   */
 -static bool binder_proc_transaction(struct binder_transaction *t,
 +static int binder_proc_transaction(struct binder_transaction *t,
                                    struct binder_proc *proc,
                                    struct binder_thread *thread)
  {
        }
  
        binder_inner_proc_lock(proc);
 +      if (proc->is_frozen) {
 +              proc->sync_recv |= !oneway;
 +              proc->async_recv |= oneway;
 +      }
  
 -      if (proc->is_dead || (thread && thread->is_dead)) {
 +      if ((proc->is_frozen && !oneway) || proc->is_dead ||
 +                      (thread && thread->is_dead)) {
                binder_inner_proc_unlock(proc);
                binder_node_unlock(node);
 -              return false;
 +              return proc->is_frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY;
        }
  
        if (!thread && !pending_async)
        if (!pending_async)
                binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
  
 +      proc->outstanding_txns++;
        binder_inner_proc_unlock(proc);
        binder_node_unlock(node);
  
 -      return true;
 +      return 0;
  }
  
  /**
@@@ -2713,7 -2700,16 +2713,16 @@@ static void binder_transaction(struct b
                u32 secid;
                size_t added_size;
  
-               security_task_getsecid(proc->tsk, &secid);
+               /*
+                * Arguably this should be the task's subjective LSM secid but
+                * we can't reliably access the subjective creds of a task
+                * other than our own so we must use the objective creds, which
+                * are safe to access.  The downside is that if a task is
+                * temporarily overriding it's creds it will not be reflected
+                * here; however, it isn't clear that binder would handle that
+                * case well anyway.
+                */
+               security_task_getsecid_obj(proc->tsk, &secid);
                ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
                if (ret) {
                        return_error = BR_FAILED_REPLY;
                        goto err_bad_object_type;
                }
        }
 -      tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
 +      if (t->buffer->oneway_spam_suspect)
 +              tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
 +      else
 +              tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
        t->work.type = BINDER_WORK_TRANSACTION;
  
        if (reply) {
                binder_enqueue_thread_work(thread, tcomplete);
                binder_inner_proc_lock(target_proc);
 -              if (target_thread->is_dead) {
 +              if (target_thread->is_dead || target_proc->is_frozen) {
 +                      return_error = target_thread->is_dead ?
 +                              BR_DEAD_REPLY : BR_FROZEN_REPLY;
                        binder_inner_proc_unlock(target_proc);
                        goto err_dead_proc_or_thread;
                }
                BUG_ON(t->buffer->async_transaction != 0);
                binder_pop_transaction_ilocked(target_thread, in_reply_to);
                binder_enqueue_thread_work_ilocked(target_thread, &t->work);
 +              target_proc->outstanding_txns++;
                binder_inner_proc_unlock(target_proc);
                wake_up_interruptible_sync(&target_thread->wait);
                binder_free_transaction(in_reply_to);
                t->from_parent = thread->transaction_stack;
                thread->transaction_stack = t;
                binder_inner_proc_unlock(proc);
 -              if (!binder_proc_transaction(t, target_proc, target_thread)) {
 +              return_error = binder_proc_transaction(t,
 +                              target_proc, target_thread);
 +              if (return_error) {
                        binder_inner_proc_lock(proc);
                        binder_pop_transaction_ilocked(thread, t);
                        binder_inner_proc_unlock(proc);
                BUG_ON(target_node == NULL);
                BUG_ON(t->buffer->async_transaction != 1);
                binder_enqueue_thread_work(thread, tcomplete);
 -              if (!binder_proc_transaction(t, target_proc, NULL))
 +              return_error = binder_proc_transaction(t, target_proc, NULL);
 +              if (return_error)
                        goto err_dead_proc_or_thread;
        }
        if (target_thread)
        return;
  
  err_dead_proc_or_thread:
 -      return_error = BR_DEAD_REPLY;
        return_error_line = __LINE__;
        binder_dequeue_work(proc, tcomplete);
  err_translate_failed:
@@@ -3717,7 -3705,7 +3726,7 @@@ static int binder_wait_for_work(struct 
                binder_inner_proc_lock(proc);
                list_del_init(&thread->waiting_thread_node);
                if (signal_pending(current)) {
 -                      ret = -ERESTARTSYS;
 +                      ret = -EINTR;
                        break;
                }
        }
@@@ -3896,14 -3884,9 +3905,14 @@@ retry
  
                        binder_stat_br(proc, thread, cmd);
                } break;
 -              case BINDER_WORK_TRANSACTION_COMPLETE: {
 +              case BINDER_WORK_TRANSACTION_COMPLETE:
 +              case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: {
 +                      if (proc->oneway_spam_detection_enabled &&
 +                                 w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT)
 +                              cmd = BR_ONEWAY_SPAM_SUSPECT;
 +                      else
 +                              cmd = BR_TRANSACTION_COMPLETE;
                        binder_inner_proc_unlock(proc);
 -                      cmd = BR_TRANSACTION_COMPLETE;
                        kfree(w);
                        binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
                        if (put_user(cmd, (uint32_t __user *)ptr))
@@@ -4324,9 -4307,6 +4333,9 @@@ static void binder_free_proc(struct bin
  
        BUG_ON(!list_empty(&proc->todo));
        BUG_ON(!list_empty(&proc->delivered_death));
 +      if (proc->outstanding_txns)
 +              pr_warn("%s: Unexpected outstanding_txns %d\n",
 +                      __func__, proc->outstanding_txns);
        device = container_of(proc->context, struct binder_device, context);
        if (refcount_dec_and_test(&device->ref)) {
                kfree(proc->context->name);
@@@ -4388,7 -4368,6 +4397,7 @@@ static int binder_thread_release(struc
                             (t->to_thread == thread) ? "in" : "out");
  
                if (t->to_thread == thread) {
 +                      thread->proc->outstanding_txns--;
                        t->to_proc = NULL;
                        t->to_thread = NULL;
                        if (t->buffer) {
@@@ -4639,76 -4618,6 +4648,76 @@@ static int binder_ioctl_get_node_debug_
        return 0;
  }
  
 +static int binder_ioctl_freeze(struct binder_freeze_info *info,
 +                             struct binder_proc *target_proc)
 +{
 +      int ret = 0;
 +
 +      if (!info->enable) {
 +              binder_inner_proc_lock(target_proc);
 +              target_proc->sync_recv = false;
 +              target_proc->async_recv = false;
 +              target_proc->is_frozen = false;
 +              binder_inner_proc_unlock(target_proc);
 +              return 0;
 +      }
 +
 +      /*
 +       * Freezing the target. Prevent new transactions by
 +       * setting frozen state. If timeout specified, wait
 +       * for transactions to drain.
 +       */
 +      binder_inner_proc_lock(target_proc);
 +      target_proc->sync_recv = false;
 +      target_proc->async_recv = false;
 +      target_proc->is_frozen = true;
 +      binder_inner_proc_unlock(target_proc);
 +
 +      if (info->timeout_ms > 0)
 +              ret = wait_event_interruptible_timeout(
 +                      target_proc->freeze_wait,
 +                      (!target_proc->outstanding_txns),
 +                      msecs_to_jiffies(info->timeout_ms));
 +
 +      if (!ret && target_proc->outstanding_txns)
 +              ret = -EAGAIN;
 +
 +      if (ret < 0) {
 +              binder_inner_proc_lock(target_proc);
 +              target_proc->is_frozen = false;
 +              binder_inner_proc_unlock(target_proc);
 +      }
 +
 +      return ret;
 +}
 +
 +static int binder_ioctl_get_freezer_info(
 +                              struct binder_frozen_status_info *info)
 +{
 +      struct binder_proc *target_proc;
 +      bool found = false;
 +
 +      info->sync_recv = 0;
 +      info->async_recv = 0;
 +
 +      mutex_lock(&binder_procs_lock);
 +      hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
 +              if (target_proc->pid == info->pid) {
 +                      found = true;
 +                      binder_inner_proc_lock(target_proc);
 +                      info->sync_recv |= target_proc->sync_recv;
 +                      info->async_recv |= target_proc->async_recv;
 +                      binder_inner_proc_unlock(target_proc);
 +              }
 +      }
 +      mutex_unlock(&binder_procs_lock);
 +
 +      if (!found)
 +              return -EINVAL;
 +
 +      return 0;
 +}
 +
  static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  {
        int ret;
                }
                break;
        }
 +      case BINDER_FREEZE: {
 +              struct binder_freeze_info info;
 +              struct binder_proc **target_procs = NULL, *target_proc;
 +              int target_procs_count = 0, i = 0;
 +
 +              ret = 0;
 +
 +              if (copy_from_user(&info, ubuf, sizeof(info))) {
 +                      ret = -EFAULT;
 +                      goto err;
 +              }
 +
 +              mutex_lock(&binder_procs_lock);
 +              hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
 +                      if (target_proc->pid == info.pid)
 +                              target_procs_count++;
 +              }
 +
 +              if (target_procs_count == 0) {
 +                      mutex_unlock(&binder_procs_lock);
 +                      ret = -EINVAL;
 +                      goto err;
 +              }
 +
 +              target_procs = kcalloc(target_procs_count,
 +                                     sizeof(struct binder_proc *),
 +                                     GFP_KERNEL);
 +
 +              if (!target_procs) {
 +                      mutex_unlock(&binder_procs_lock);
 +                      ret = -ENOMEM;
 +                      goto err;
 +              }
 +
 +              hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
 +                      if (target_proc->pid != info.pid)
 +                              continue;
 +
 +                      binder_inner_proc_lock(target_proc);
 +                      target_proc->tmp_ref++;
 +                      binder_inner_proc_unlock(target_proc);
 +
 +                      target_procs[i++] = target_proc;
 +              }
 +              mutex_unlock(&binder_procs_lock);
 +
 +              for (i = 0; i < target_procs_count; i++) {
 +                      if (ret >= 0)
 +                              ret = binder_ioctl_freeze(&info,
 +                                                        target_procs[i]);
 +
 +                      binder_proc_dec_tmpref(target_procs[i]);
 +              }
 +
 +              kfree(target_procs);
 +
 +              if (ret < 0)
 +                      goto err;
 +              break;
 +      }
 +      case BINDER_GET_FROZEN_INFO: {
 +              struct binder_frozen_status_info info;
 +
 +              if (copy_from_user(&info, ubuf, sizeof(info))) {
 +                      ret = -EFAULT;
 +                      goto err;
 +              }
 +
 +              ret = binder_ioctl_get_freezer_info(&info);
 +              if (ret < 0)
 +                      goto err;
 +
 +              if (copy_to_user(ubuf, &info, sizeof(info))) {
 +                      ret = -EFAULT;
 +                      goto err;
 +              }
 +              break;
 +      }
 +      case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: {
 +              uint32_t enable;
 +
 +              if (copy_from_user(&enable, ubuf, sizeof(enable))) {
 +                      ret = -EINVAL;
 +                      goto err;
 +              }
 +              binder_inner_proc_lock(proc);
 +              proc->oneway_spam_detection_enabled = (bool)enable;
 +              binder_inner_proc_unlock(proc);
 +              break;
 +      }
        default:
                ret = -EINVAL;
                goto err;
@@@ -4926,7 -4745,7 +4935,7 @@@ err
        if (thread)
                thread->looper_need_return = false;
        wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
 -      if (ret && ret != -ERESTARTSYS)
 +      if (ret && ret != -EINTR)
                pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
  err_unlocked:
        trace_binder_ioctl_done(ret);
@@@ -5013,7 -4832,6 +5022,7 @@@ static int binder_open(struct inode *no
        get_task_struct(current->group_leader);
        proc->tsk = current->group_leader;
        INIT_LIST_HEAD(&proc->todo);
 +      init_waitqueue_head(&proc->freeze_wait);
        proc->default_priority = task_nice(current);
        /* binderfs stashes devices in i_private */
        if (is_binderfs_device(nodp)) {
@@@ -5226,9 -5044,6 +5235,9 @@@ static void binder_deferred_release(str
        proc->tmp_ref++;
  
        proc->is_dead = true;
 +      proc->is_frozen = false;
 +      proc->sync_recv = false;
 +      proc->async_recv = false;
        threads = 0;
        active_transactions = 0;
        while ((n = rb_first(&proc->threads))) {
@@@ -5579,9 -5394,7 +5588,9 @@@ static const char * const binder_return
        "BR_FINISHED",
        "BR_DEAD_BINDER",
        "BR_CLEAR_DEATH_NOTIFICATION_DONE",
 -      "BR_FAILED_REPLY"
 +      "BR_FAILED_REPLY",
 +      "BR_FROZEN_REPLY",
 +      "BR_ONEWAY_SPAM_SUSPECT",
  };
  
  static const char * const binder_command_strings[] = {
diff --combined fs/nfs/internal.h
index 7b644d6c09e4b27d8e0e5a25d75f91202ea72049,c3f57b423611358141749e5515e52627f33e3f82..7395d0977b7daa3c90bb69603979c8d746103ed9
@@@ -96,6 -96,7 +96,7 @@@ struct nfs_fs_context 
        char                    *fscache_uniq;
        unsigned short          protofamily;
        unsigned short          mountfamily;
+       bool                    has_sec_mnt_opts;
  
        struct {
                union {
@@@ -411,8 -412,7 +412,8 @@@ extern int nfs_write_inode(struct inod
  extern int nfs_drop_inode(struct inode *);
  extern void nfs_clear_inode(struct inode *);
  extern void nfs_evict_inode(struct inode *);
 -void nfs_zap_acl_cache(struct inode *inode);
 +extern void nfs_zap_acl_cache(struct inode *inode);
 +extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags);
  extern bool nfs_check_cache_invalid(struct inode *, unsigned long);
  extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
  extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
index 7650de0485700d4dd32d83e9e02d884b316fdc2e,7130c9648ad1b7179c7c43eb669a6bee2297ca51..ac0ece01305a65744af9a619bc3fb5afed7cfde2
@@@ -219,21 -219,14 +219,21 @@@ static inline bool selinux_policycap_ge
        return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS]);
  }
  
 +struct selinux_policy_convert_data;
 +
 +struct selinux_load_state {
 +      struct selinux_policy *policy;
 +      struct selinux_policy_convert_data *convert_data;
 +};
 +
  int security_mls_enabled(struct selinux_state *state);
  int security_load_policy(struct selinux_state *state,
 -                      void *data, size_t len,
 -                      struct selinux_policy **newpolicyp);
 +                       void *data, size_t len,
 +                       struct selinux_load_state *load_state);
  void selinux_policy_commit(struct selinux_state *state,
 -                      struct selinux_policy *newpolicy);
 +                         struct selinux_load_state *load_state);
  void selinux_policy_cancel(struct selinux_state *state,
 -                      struct selinux_policy *policy);
 +                         struct selinux_load_state *load_state);
  int security_read_policy(struct selinux_state *state,
                         void **data, size_t *len);
  int security_read_state_kernel(struct selinux_state *state,
@@@ -426,7 -419,7 +426,7 @@@ extern struct page *selinux_kernel_stat
  
  #define SELINUX_KERNEL_STATUS_VERSION 1
  struct selinux_kernel_status {
-       u32     version;        /* version number of thie structure */
+       u32     version;        /* version number of the structure */
        u32     sequence;       /* sequence number of seqlock logic */
        u32     enforcing;      /* current setting of enforcing mode */
        u32     policyload;     /* times of policy reloaded */
index fff6babeeae669d6d40a8325d54c921d4c537706,dc9d7674f59281a92e0b0d2fc1049acad985acb4..e4cd7cb856f375c8eaab1894f632374510709bf2
@@@ -41,6 -41,7 +41,7 @@@
  #include "security.h"
  #include "objsec.h"
  #include "conditional.h"
+ #include "ima.h"
  
  enum sel_inos {
        SEL_ROOT_INO = 2,
@@@ -182,6 -183,8 +183,8 @@@ static ssize_t sel_write_enforce(struc
                selinux_status_update_setenforce(state, new_value);
                if (!new_value)
                        call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
+               selinux_ima_measure_state(state);
        }
        length = count;
  out:
@@@ -563,13 -566,17 +566,13 @@@ static int sel_make_policy_nodes(struc
  
        ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num,
                             &tmp_bool_names, &tmp_bool_values);
 -      if (ret) {
 -              pr_err("SELinux: failed to load policy booleans\n");
 +      if (ret)
                goto out;
 -      }
  
        ret = sel_make_classes(newpolicy, tmp_class_dir,
                               &fsi->last_class_ino);
 -      if (ret) {
 -              pr_err("SELinux: failed to load policy classes\n");
 +      if (ret)
                goto out;
 -      }
  
        /* booleans */
        old_dentry = fsi->bool_dir;
@@@ -612,7 -619,7 +615,7 @@@ static ssize_t sel_write_load(struct fi
  
  {
        struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
 -      struct selinux_policy *newpolicy;
 +      struct selinux_load_state load_state;
        ssize_t length;
        void *data = NULL;
  
        if (copy_from_user(data, buf, count) != 0)
                goto out;
  
 -      length = security_load_policy(fsi->state, data, count, &newpolicy);
 +      length = security_load_policy(fsi->state, data, count, &load_state);
        if (length) {
                pr_warn_ratelimited("SELinux: failed to load policy\n");
                goto out;
        }
  
 -      length = sel_make_policy_nodes(fsi, newpolicy);
 +      length = sel_make_policy_nodes(fsi, load_state.policy);
        if (length) {
 -              selinux_policy_cancel(fsi->state, newpolicy);
 -              goto out1;
 +              pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n");
 +              selinux_policy_cancel(fsi->state, &load_state);
 +              goto out;
        }
  
 -      selinux_policy_commit(fsi->state, newpolicy);
 +      selinux_policy_commit(fsi->state, &load_state);
  
        length = count;
  
 -out1:
        audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
                "auid=%u ses=%u lsm=selinux res=1",
                from_kuid(&init_user_ns, audit_get_loginuid(current)),
@@@ -758,6 -765,9 +761,9 @@@ static ssize_t sel_write_checkreqprot(s
  
        checkreqprot_set(fsi->state, (new_value ? 1 : 0));
        length = count;
+       selinux_ima_measure_state(fsi->state);
  out:
        kfree(page);
        return length;
index 30163314504053e8a97943f1ef8a049cdf3e7e22,f37375c9553fa6afbfe8c0ec6ee56c0c2670f594..f0ba826113437a1ed79dee31908eafccc81a8859
  #include "policycap_names.h"
  #include "ima.h"
  
 +struct convert_context_args {
 +      struct selinux_state *state;
 +      struct policydb *oldp;
 +      struct policydb *newp;
 +};
 +
 +struct selinux_policy_convert_data {
 +      struct convert_context_args args;
 +      struct sidtab_convert_params sidtab_params;
 +};
 +
  /* Forward declaration. */
  static int context_struct_to_string(struct policydb *policydb,
                                    struct context *context,
@@@ -1552,7 -1541,6 +1552,7 @@@ static int security_context_to_sid_core
                if (!str)
                        goto out;
        }
 +retry:
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
        } else if (rc)
                goto out_unlock;
        rc = sidtab_context_to_sid(sidtab, &context, sid);
 +      if (rc == -ESTALE) {
 +              rcu_read_unlock();
 +              if (context.str) {
 +                      str = context.str;
 +                      context.str = NULL;
 +              }
 +              context_destroy(&context);
 +              goto retry;
 +      }
        context_destroy(&context);
  out_unlock:
        rcu_read_unlock();
@@@ -1724,7 -1703,7 +1724,7 @@@ static int security_compute_sid(struct 
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
 -      struct class_datum *cladatum = NULL;
 +      struct class_datum *cladatum;
        struct context *scontext, *tcontext, newcontext;
        struct sidtab_entry *sentry, *tentry;
        struct avtab_key avkey;
                goto out;
        }
  
 +retry:
 +      cladatum = NULL;
        context_init(&newcontext);
  
        rcu_read_lock();
        }
        /* Obtain the sid for the context. */
        rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
 +      if (rc == -ESTALE) {
 +              rcu_read_unlock();
 +              context_destroy(&newcontext);
 +              goto retry;
 +      }
  out_unlock:
        rcu_read_unlock();
        context_destroy(&newcontext);
@@@ -2002,6 -1974,12 +2002,6 @@@ static inline int convert_context_handl
        return 0;
  }
  
 -struct convert_context_args {
 -      struct selinux_state *state;
 -      struct policydb *oldp;
 -      struct policydb *newp;
 -};
 -
  /*
   * Convert the values in the security context
   * structure `oldc' from the values specified
@@@ -2181,7 -2159,7 +2181,7 @@@ static void selinux_policy_cond_free(st
  }
  
  void selinux_policy_cancel(struct selinux_state *state,
 -                      struct selinux_policy *policy)
 +                         struct selinux_load_state *load_state)
  {
        struct selinux_policy *oldpolicy;
  
                                        lockdep_is_held(&state->policy_mutex));
  
        sidtab_cancel_convert(oldpolicy->sidtab);
 -      selinux_policy_free(policy);
 +      selinux_policy_free(load_state->policy);
 +      kfree(load_state->convert_data);
  }
  
  static void selinux_notify_policy_change(struct selinux_state *state,
        selinux_status_update_policyload(state, seqno);
        selinux_netlbl_cache_invalidate();
        selinux_xfrm_notify_policyload();
-       selinux_ima_measure_state(state);
+       selinux_ima_measure_state_locked(state);
  }
  
  void selinux_policy_commit(struct selinux_state *state,
 -                      struct selinux_policy *newpolicy)
 +                         struct selinux_load_state *load_state)
  {
 -      struct selinux_policy *oldpolicy;
 +      struct selinux_policy *oldpolicy, *newpolicy = load_state->policy;
 +      unsigned long flags;
        u32 seqno;
  
        oldpolicy = rcu_dereference_protected(state->policy,
        seqno = newpolicy->latest_granting;
  
        /* Install the new policy. */
 -      rcu_assign_pointer(state->policy, newpolicy);
 +      if (oldpolicy) {
 +              sidtab_freeze_begin(oldpolicy->sidtab, &flags);
 +              rcu_assign_pointer(state->policy, newpolicy);
 +              sidtab_freeze_end(oldpolicy->sidtab, &flags);
 +      } else {
 +              rcu_assign_pointer(state->policy, newpolicy);
 +      }
  
        /* Load the policycaps from the new policy */
        security_load_policycaps(state, newpolicy);
        /* Free the old policy */
        synchronize_rcu();
        selinux_policy_free(oldpolicy);
 +      kfree(load_state->convert_data);
  
        /* Notify others of the policy change */
        selinux_notify_policy_change(state, seqno);
   * loading the new policy.
   */
  int security_load_policy(struct selinux_state *state, void *data, size_t len,
 -                      struct selinux_policy **newpolicyp)
 +                       struct selinux_load_state *load_state)
  {
        struct selinux_policy *newpolicy, *oldpolicy;
 -      struct sidtab_convert_params convert_params;
 -      struct convert_context_args args;
 +      struct selinux_policy_convert_data *convert_data;
        int rc = 0;
        struct policy_file file = { data, len }, *fp = &file;
  
                goto err_mapping;
        }
  
 -
        if (!selinux_initialized(state)) {
                /* First policy load, so no need to preserve state from old policy */
 -              *newpolicyp = newpolicy;
 +              load_state->policy = newpolicy;
 +              load_state->convert_data = NULL;
                return 0;
        }
  
                goto err_free_isids;
        }
  
 +      convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL);
 +      if (!convert_data) {
 +              rc = -ENOMEM;
 +              goto err_free_isids;
 +      }
 +
        /*
         * Convert the internal representations of contexts
         * in the new SID table.
         */
 -      args.state = state;
 -      args.oldp = &oldpolicy->policydb;
 -      args.newp = &newpolicy->policydb;
 +      convert_data->args.state = state;
 +      convert_data->args.oldp = &oldpolicy->policydb;
 +      convert_data->args.newp = &newpolicy->policydb;
  
 -      convert_params.func = convert_context;
 -      convert_params.args = &args;
 -      convert_params.target = newpolicy->sidtab;
 +      convert_data->sidtab_params.func = convert_context;
 +      convert_data->sidtab_params.args = &convert_data->args;
 +      convert_data->sidtab_params.target = newpolicy->sidtab;
  
 -      rc = sidtab_convert(oldpolicy->sidtab, &convert_params);
 +      rc = sidtab_convert(oldpolicy->sidtab, &convert_data->sidtab_params);
        if (rc) {
                pr_err("SELinux:  unable to convert the internal"
                        " representation of contexts in the new SID"
                        " table\n");
 -              goto err_free_isids;
 +              goto err_free_convert_data;
        }
  
 -      *newpolicyp = newpolicy;
 +      load_state->policy = newpolicy;
 +      load_state->convert_data = convert_data;
        return 0;
  
 +err_free_convert_data:
 +      kfree(convert_data);
  err_free_isids:
        sidtab_destroy(newpolicy->sidtab);
  err_mapping:
@@@ -2381,15 -2342,13 +2381,15 @@@ int security_port_sid(struct selinux_st
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
 -      int rc = 0;
 +      int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_PORT;
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
@@@ -2438,15 -2393,13 +2438,15 @@@ int security_ib_pkey_sid(struct selinux
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
 -      int rc = 0;
 +      int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_UNLABELED;
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                        rc = sidtab_context_to_sid(sidtab,
                                                   &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
@@@ -2496,15 -2445,13 +2496,15 @@@ int security_ib_endport_sid(struct seli
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
 -      int rc = 0;
 +      int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_UNLABELED;
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
@@@ -2552,7 -2495,7 +2552,7 @@@ int security_netif_sid(struct selinux_s
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
 -      int rc = 0;
 +      int rc;
        struct ocontext *c;
  
        if (!selinux_initialized(state)) {
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0] || !c->sid[1]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                        rc = sidtab_context_to_sid(sidtab, &c->context[1],
                                                   &c->sid[1]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
@@@ -2639,7 -2572,6 +2639,7 @@@ int security_node_sid(struct selinux_st
                return 0;
        }
  
 +retry:
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                        rc = sidtab_context_to_sid(sidtab,
                                                   &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
@@@ -2733,24 -2661,18 +2733,24 @@@ int security_get_user_sids(struct selin
        struct sidtab *sidtab;
        struct context *fromcon, usercon;
        u32 *mysids = NULL, *mysids2, sid;
 -      u32 mynel = 0, maxnel = SIDS_NEL;
 +      u32 i, j, mynel, maxnel = SIDS_NEL;
        struct user_datum *user;
        struct role_datum *role;
        struct ebitmap_node *rnode, *tnode;
 -      int rc = 0, i, j;
 +      int rc;
  
        *sids = NULL;
        *nel = 0;
  
        if (!selinux_initialized(state))
 -              goto out;
 +              return 0;
 +
 +      mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL);
 +      if (!mysids)
 +              return -ENOMEM;
  
 +retry:
 +      mynel = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
  
        usercon.user = user->value;
  
 -      rc = -ENOMEM;
 -      mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
 -      if (!mysids)
 -              goto out_unlock;
 -
        ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
                role = policydb->role_val_to_struct[i];
                usercon.role = i + 1;
                                continue;
  
                        rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out_unlock;
                        if (mynel < maxnel) {
@@@ -2807,14 -2730,14 +2807,14 @@@ out_unlock
        rcu_read_unlock();
        if (rc || !mynel) {
                kfree(mysids);
 -              goto out;
 +              return rc;
        }
  
        rc = -ENOMEM;
        mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
        if (!mysids2) {
                kfree(mysids);
 -              goto out;
 +              return rc;
        }
        for (i = 0, j = 0; i < mynel; i++) {
                struct av_decision dummy_avd;
                        mysids2[j++] = mysids[i];
                cond_resched();
        }
 -      rc = 0;
        kfree(mysids);
        *sids = mysids2;
        *nel = j;
 -out:
 -      return rc;
 +      return 0;
  }
  
  /**
   * Obtain a SID to use for a file in a filesystem that
   * cannot support xattr or use a fixed labeling behavior like
   * transition SIDs or task SIDs.
 + *
 + * WARNING: This function may return -ESTALE, indicating that the caller
 + * must retry the operation after re-acquiring the policy pointer!
   */
  static inline int __security_genfs_sid(struct selinux_policy *policy,
                                       const char *fstype,
@@@ -2924,13 -2846,11 +2924,13 @@@ int security_genfs_sid(struct selinux_s
                return 0;
        }
  
 -      rcu_read_lock();
 -      policy = rcu_dereference(state->policy);
 -      retval = __security_genfs_sid(policy,
 -                              fstype, path, orig_sclass, sid);
 -      rcu_read_unlock();
 +      do {
 +              rcu_read_lock();
 +              policy = rcu_dereference(state->policy);
 +              retval = __security_genfs_sid(policy, fstype, path,
 +                                            orig_sclass, sid);
 +              rcu_read_unlock();
 +      } while (retval == -ESTALE);
        return retval;
  }
  
@@@ -2953,7 -2873,7 +2953,7 @@@ int security_fs_use(struct selinux_stat
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
 -      int rc = 0;
 +      int rc;
        struct ocontext *c;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *fstype = sb->s_type->name;
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
 +                      if (rc == -ESTALE) {
 +                              rcu_read_unlock();
 +                              goto retry;
 +                      }
                        if (rc)
                                goto out;
                }
        } else {
                rc = __security_genfs_sid(policy, fstype, "/",
                                        SECCLASS_DIR, &sbsec->sid);
 +              if (rc == -ESTALE) {
 +                      rcu_read_unlock();
 +                      goto retry;
 +              }
                if (rc) {
                        sbsec->behavior = SECURITY_FS_USE_NONE;
                        rc = 0;
@@@ -3207,13 -3117,12 +3207,13 @@@ int security_sid_mls_copy(struct selinu
        u32 len;
        int rc;
  
 -      rc = 0;
        if (!selinux_initialized(state)) {
                *new_sid = sid;
 -              goto out;
 +              return 0;
        }
  
 +retry:
 +      rc = 0;
        context_init(&newcon);
  
        rcu_read_lock();
                }
        }
        rc = sidtab_context_to_sid(sidtab, &newcon, new_sid);
 +      if (rc == -ESTALE) {
 +              rcu_read_unlock();
 +              context_destroy(&newcon);
 +              goto retry;
 +      }
  out_unlock:
        rcu_read_unlock();
        context_destroy(&newcon);
 -out:
        return rc;
  }
  
@@@ -3872,8 -3777,6 +3872,8 @@@ int security_netlbl_secattr_to_sid(stru
                return 0;
        }
  
 +retry:
 +      rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                                goto out;
                }
                rc = -EIDRM;
 -              if (!mls_context_isvalid(policydb, &ctx_new))
 -                      goto out_free;
 +              if (!mls_context_isvalid(policydb, &ctx_new)) {
 +                      ebitmap_destroy(&ctx_new.range.level[0].cat);
 +                      goto out;
 +              }
  
                rc = sidtab_context_to_sid(sidtab, &ctx_new, sid);
 +              ebitmap_destroy(&ctx_new.range.level[0].cat);
 +              if (rc == -ESTALE) {
 +                      rcu_read_unlock();
 +                      goto retry;
 +              }
                if (rc)
 -                      goto out_free;
 +                      goto out;
  
                security_netlbl_cache_add(secattr, *sid);
 -
 -              ebitmap_destroy(&ctx_new.range.level[0].cat);
        } else
                *sid = SECSID_NULL;
  
 -      rcu_read_unlock();
 -      return 0;
 -out_free:
 -      ebitmap_destroy(&ctx_new.range.level[0].cat);
  out:
        rcu_read_unlock();
        return rc;
This page took 0.155051 seconds and 4 git commands to generate.