diff mbox series

selinux: make cleanup on error consistent

Message ID 20230504141330.1557243-1-juraj@jurajmarcin.com (mailing list archive)
State Rejected
Delegated to: Paul Moore
Headers show
Series selinux: make cleanup on error consistent | expand

Commit Message

Juraj Marcin May 4, 2023, 2:13 p.m. UTC
The file security.c contains two functions (security_read_policy() and
security_read_state_kernel()) that are almost identical, but their
cleanup conventions differ.

This patch unifies the behavior by adding cleanup to
security_read_policy() in case some call inside it fails instead of
relying on the caller to properly free the memory. On top of that, this
patch future-proofs both functions by adding local variables and
modifying the pointers only in case of a success.

Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com>
---
 security/selinux/include/security.h |  4 +--
 security/selinux/selinuxfs.c        |  2 --
 security/selinux/ss/services.c      | 50 +++++++++++++++++++----------
 3 files changed, 35 insertions(+), 21 deletions(-)

Comments

Paul Moore May 4, 2023, 9:03 p.m. UTC | #1
On Thu, May 4, 2023 at 10:14 AM Juraj Marcin <juraj@jurajmarcin.com> wrote:
>
> The file security.c contains two functions (security_read_policy() and
> security_read_state_kernel()) that are almost identical, but their
> cleanup conventions differ.
>
> This patch unifies the behavior by adding cleanup to
> security_read_policy() in case some call inside it fails instead of
> relying on the caller to properly free the memory. On top of that, this
> patch future-proofs both functions by adding local variables and
> modifying the pointers only in case of a success.
>
> Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com>
> ---
>  security/selinux/include/security.h |  4 +--
>  security/selinux/selinuxfs.c        |  2 --
>  security/selinux/ss/services.c      | 50 +++++++++++++++++++----------
>  3 files changed, 35 insertions(+), 21 deletions(-)
>
> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> index 8746fafeb7789..2990b3d08236d 100644
> --- a/security/selinux/include/security.h
> +++ b/security/selinux/include/security.h
> @@ -213,8 +213,8 @@ int security_load_policy(void *data, size_t len,
>                          struct selinux_load_state *load_state);
>  void selinux_policy_commit(struct selinux_load_state *load_state);
>  void selinux_policy_cancel(struct selinux_load_state *load_state);
> -int security_read_policy(void **data, size_t *len);
> -int security_read_state_kernel(void **data, size_t *len);
> +int security_read_policy(void **pdata, size_t *plen);
> +int security_read_state_kernel(void **pdata, size_t *plen);
>  int security_policycap_supported(unsigned int req_cap);
>
>  #define SEL_VEC_MAX 32
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 69a583b91fc57..6d4cd66360739 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -406,8 +406,6 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
>  err:
>         mutex_unlock(&selinux_state.policy_mutex);
>
> -       if (plm)
> -               vfree(plm->data);
>         kfree(plm);
>         return rc;
>  }
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index f14d1ffe54c5d..f2fd2b6510560 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -3941,12 +3941,16 @@ static int __security_read_policy(struct selinux_policy *policy,
>
>  /**
>   * security_read_policy - read the policy.
> - * @data: binary policy data
> - * @len: length of data in bytes
> + * @pdata: binary policy data
> + * @plen: length of data in bytes
>   *
> + * In case of a failure, the pointers are not modified.
>   */
> -int security_read_policy(void **data, size_t *len)
> +int security_read_policy(void **pdata, size_t *plen)
>  {
> +       int err;
> +       void *data;
> +       size_t len;
>         struct selinux_state *state = &selinux_state;
>         struct selinux_policy *policy;
>
> @@ -3955,28 +3959,39 @@ int security_read_policy(void **data, size_t *len)
>         if (!policy)
>                 return -EINVAL;
>
> -       *len = policy->policydb.len;
> -       *data = vmalloc_user(*len);
> -       if (!*data)
> +       len = policy->policydb.len;
> +       data = vmalloc_user(len);
> +       if (!data)
>                 return -ENOMEM;
>
> -       return __security_read_policy(policy, *data, len);
> +       err = __security_read_policy(policy, data, &len);
> +       if (err) {
> +               vfree(data);
> +               return err;
> +       }
> +       *pdata = data;
> +       *plen = len;
> +       return err;
>  }

Hi Juraj,

Thank you for your patch, but I prefer the code as it is currently
written.  I feel it is more maintainable to free the @data buffer at
the same time as we free the policy_load_memory struct which contains
the pointer to the @data buffer.

If there was a possibility that we could unify security_read_policy()
and security_read_state_kernel() then it might be worthwhile to
shuffle some of the allocations, but given the need to allocate a user
pointer in security_read_policy() and a desire preserve a level of
abstraction* between the security server and the rest of the SELinux
code I don't think this change is something I want to merge.  I'm
sorry.

* As time goes on, I'm less and less interested in preserving the
separation between the SELinux security server and the SELinux
Linux/hooks code, however there are some that feel strongly about this
so we'll stick with it for now.
diff mbox series

Patch

diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 8746fafeb7789..2990b3d08236d 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -213,8 +213,8 @@  int security_load_policy(void *data, size_t len,
 			 struct selinux_load_state *load_state);
 void selinux_policy_commit(struct selinux_load_state *load_state);
 void selinux_policy_cancel(struct selinux_load_state *load_state);
-int security_read_policy(void **data, size_t *len);
-int security_read_state_kernel(void **data, size_t *len);
+int security_read_policy(void **pdata, size_t *plen);
+int security_read_state_kernel(void **pdata, size_t *plen);
 int security_policycap_supported(unsigned int req_cap);
 
 #define SEL_VEC_MAX 32
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 69a583b91fc57..6d4cd66360739 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -406,8 +406,6 @@  static int sel_open_policy(struct inode *inode, struct file *filp)
 err:
 	mutex_unlock(&selinux_state.policy_mutex);
 
-	if (plm)
-		vfree(plm->data);
 	kfree(plm);
 	return rc;
 }
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f14d1ffe54c5d..f2fd2b6510560 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3941,12 +3941,16 @@  static int __security_read_policy(struct selinux_policy *policy,
 
 /**
  * security_read_policy - read the policy.
- * @data: binary policy data
- * @len: length of data in bytes
+ * @pdata: binary policy data
+ * @plen: length of data in bytes
  *
+ * In case of a failure, the pointers are not modified.
  */
-int security_read_policy(void **data, size_t *len)
+int security_read_policy(void **pdata, size_t *plen)
 {
+	int err;
+	void *data;
+	size_t len;
 	struct selinux_state *state = &selinux_state;
 	struct selinux_policy *policy;
 
@@ -3955,28 +3959,39 @@  int security_read_policy(void **data, size_t *len)
 	if (!policy)
 		return -EINVAL;
 
-	*len = policy->policydb.len;
-	*data = vmalloc_user(*len);
-	if (!*data)
+	len = policy->policydb.len;
+	data = vmalloc_user(len);
+	if (!data)
 		return -ENOMEM;
 
-	return __security_read_policy(policy, *data, len);
+	err = __security_read_policy(policy, data, &len);
+	if (err) {
+		vfree(data);
+		return err;
+	}
+	*pdata = data;
+	*plen = len;
+	return err;
 }
 
 /**
  * security_read_state_kernel - read the policy.
- * @data: binary policy data
- * @len: length of data in bytes
+ * @pdata: binary policy data
+ * @plen: length of data in bytes
  *
  * Allocates kernel memory for reading SELinux policy.
  * This function is for internal use only and should not
  * be used for returning data to user space.
  *
+ * In case of a failure, the pointers are not modified.
+ *
  * This function must be called with policy_mutex held.
  */
-int security_read_state_kernel(void **data, size_t *len)
+int security_read_state_kernel(void **pdata, size_t *plen)
 {
 	int err;
+	void *data;
+	size_t len;
 	struct selinux_state *state = &selinux_state;
 	struct selinux_policy *policy;
 
@@ -3985,16 +4000,17 @@  int security_read_state_kernel(void **data, size_t *len)
 	if (!policy)
 		return -EINVAL;
 
-	*len = policy->policydb.len;
-	*data = vmalloc(*len);
-	if (!*data)
+	len = policy->policydb.len;
+	data = vmalloc(len);
+	if (!data)
 		return -ENOMEM;
 
-	err = __security_read_policy(policy, *data, len);
+	err = __security_read_policy(policy, data, &len);
 	if (err) {
-		vfree(*data);
-		*data = NULL;
-		*len = 0;
+		vfree(data);
+		return err;
 	}
+	*pdata = data;
+	*plen = len;
 	return err;
 }