[PATCH v2 07/12] xen/arm: ffa: Fix RXTX_UNMAP ownership race

Bertrand Marquis posted 12 patches 2 months ago
[PATCH v2 07/12] xen/arm: ffa: Fix RXTX_UNMAP ownership race
Posted by Bertrand Marquis 2 months ago
rxtx_unmap() checks RX ownership without holding the RX/TX locks and
only enforces the ownership rule when FFA_RX_ACQUIRE is supported. This
allows a vCPU to acquire RX between the check and unmap, and it lets
RXTX_UNMAP proceed while RX is owned when buffers are not forwarded to
firmware.

Hold rx_lock/tx_lock across the ownership check and unmap, and deny
RXTX_UNMAP whenever RX is owned, independent of RX_ACQUIRE support. For
teardown, release RX ownership under the same lock window; use
FFA_RX_RELEASE directly because rx_lock is held, and clear the local
flag when the firmware path is unavailable.

Functional impact: RXTX_UNMAP now reliably returns DENIED while RX is
owned, and teardown releases/clears ownership without a race.

Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
---
Changes since v1:
- Remove marking rx buffer as free during teardown as it is useless
- Add a comment when calling rxtx_unmap during teardown to remind code
  readers that true is for teardown mode
---
 xen/arch/arm/tee/ffa_rxtx.c | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/xen/arch/arm/tee/ffa_rxtx.c b/xen/arch/arm/tee/ffa_rxtx.c
index eff95a7955d7..c4fc4c4934af 100644
--- a/xen/arch/arm/tee/ffa_rxtx.c
+++ b/xen/arch/arm/tee/ffa_rxtx.c
@@ -220,7 +220,7 @@ err_unlock_rxtx:
     return ret;
 }
 
-static int32_t rxtx_unmap(struct domain *d)
+static int32_t rxtx_unmap(struct domain *d, bool teardown)
 {
     struct ffa_ctx *ctx = d->arch.tee;
     int32_t ret = FFA_RET_OK;
@@ -234,6 +234,32 @@ static int32_t rxtx_unmap(struct domain *d)
         goto err_unlock_rxtx;
     }
 
+    if ( !ctx->rx_is_free )
+    {
+        if ( teardown )
+        {
+            if ( ffa_fw_supports_fid(FFA_RX_ACQUIRE) )
+            {
+                int32_t rel_ret;
+
+                /* Can't use ffa_rx_release() while holding rx_lock. */
+                rel_ret = ffa_simple_call(FFA_RX_RELEASE, ctx->ffa_id,
+                                          0, 0, 0);
+                if ( rel_ret )
+                    gdprintk(XENLOG_DEBUG,
+                             "ffa: RX release during teardown failed: %d\n",
+                             rel_ret);
+            }
+        }
+        else
+        {
+            gdprintk(XENLOG_DEBUG,
+                     "ffa: RXTX_UNMAP denied, RX buffer owned by VM\n");
+            ret = FFA_RET_DENIED;
+            goto err_unlock_rxtx;
+        }
+    }
+
     if ( ffa_fw_supports_fid(FFA_RX_ACQUIRE) )
     {
         ret = ffa_rxtx_unmap(ffa_get_vm_id(d));
@@ -261,7 +287,7 @@ err_unlock_rxtx:
 
 int32_t ffa_handle_rxtx_unmap(void)
 {
-    return rxtx_unmap(current->domain);
+    return rxtx_unmap(current->domain, false);
 }
 
 int32_t ffa_rx_acquire(struct ffa_ctx *ctx, void **buf, size_t *buf_size)
@@ -369,7 +395,7 @@ int32_t ffa_rxtx_domain_init(struct domain *d)
 
 void ffa_rxtx_domain_destroy(struct domain *d)
 {
-    rxtx_unmap(d);
+    rxtx_unmap(d, true /* teardown */);
 }
 
 void *ffa_rxtx_spmc_rx_acquire(void)
-- 
2.52.0
Re: [PATCH v2 07/12] xen/arm: ffa: Fix RXTX_UNMAP ownership race
Posted by Jens Wiklander 1 month, 4 weeks ago
Hi Bertrand,

On Wed, Feb 11, 2026 at 6:16 PM Bertrand Marquis
<bertrand.marquis@arm.com> wrote:
>
> rxtx_unmap() checks RX ownership without holding the RX/TX locks and
> only enforces the ownership rule when FFA_RX_ACQUIRE is supported. This
> allows a vCPU to acquire RX between the check and unmap, and it lets
> RXTX_UNMAP proceed while RX is owned when buffers are not forwarded to
> firmware.
>
> Hold rx_lock/tx_lock across the ownership check and unmap, and deny
> RXTX_UNMAP whenever RX is owned, independent of RX_ACQUIRE support. For
> teardown, release RX ownership under the same lock window; use
> FFA_RX_RELEASE directly because rx_lock is held, and clear the local
> flag when the firmware path is unavailable.
>
> Functional impact: RXTX_UNMAP now reliably returns DENIED while RX is
> owned, and teardown releases/clears ownership without a race.
>
> Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
> ---
> Changes since v1:
> - Remove marking rx buffer as free during teardown as it is useless
> - Add a comment when calling rxtx_unmap during teardown to remind code
>   readers that true is for teardown mode
> ---
>  xen/arch/arm/tee/ffa_rxtx.c | 32 +++++++++++++++++++++++++++++---
>  1 file changed, 29 insertions(+), 3 deletions(-)

Looks good:
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>

Cheers,
Jens

>
> diff --git a/xen/arch/arm/tee/ffa_rxtx.c b/xen/arch/arm/tee/ffa_rxtx.c
> index eff95a7955d7..c4fc4c4934af 100644
> --- a/xen/arch/arm/tee/ffa_rxtx.c
> +++ b/xen/arch/arm/tee/ffa_rxtx.c
> @@ -220,7 +220,7 @@ err_unlock_rxtx:
>      return ret;
>  }
>
> -static int32_t rxtx_unmap(struct domain *d)
> +static int32_t rxtx_unmap(struct domain *d, bool teardown)
>  {
>      struct ffa_ctx *ctx = d->arch.tee;
>      int32_t ret = FFA_RET_OK;
> @@ -234,6 +234,32 @@ static int32_t rxtx_unmap(struct domain *d)
>          goto err_unlock_rxtx;
>      }
>
> +    if ( !ctx->rx_is_free )
> +    {
> +        if ( teardown )
> +        {
> +            if ( ffa_fw_supports_fid(FFA_RX_ACQUIRE) )
> +            {
> +                int32_t rel_ret;
> +
> +                /* Can't use ffa_rx_release() while holding rx_lock. */
> +                rel_ret = ffa_simple_call(FFA_RX_RELEASE, ctx->ffa_id,
> +                                          0, 0, 0);
> +                if ( rel_ret )
> +                    gdprintk(XENLOG_DEBUG,
> +                             "ffa: RX release during teardown failed: %d\n",
> +                             rel_ret);
> +            }
> +        }
> +        else
> +        {
> +            gdprintk(XENLOG_DEBUG,
> +                     "ffa: RXTX_UNMAP denied, RX buffer owned by VM\n");
> +            ret = FFA_RET_DENIED;
> +            goto err_unlock_rxtx;
> +        }
> +    }
> +
>      if ( ffa_fw_supports_fid(FFA_RX_ACQUIRE) )
>      {
>          ret = ffa_rxtx_unmap(ffa_get_vm_id(d));
> @@ -261,7 +287,7 @@ err_unlock_rxtx:
>
>  int32_t ffa_handle_rxtx_unmap(void)
>  {
> -    return rxtx_unmap(current->domain);
> +    return rxtx_unmap(current->domain, false);
>  }
>
>  int32_t ffa_rx_acquire(struct ffa_ctx *ctx, void **buf, size_t *buf_size)
> @@ -369,7 +395,7 @@ int32_t ffa_rxtx_domain_init(struct domain *d)
>
>  void ffa_rxtx_domain_destroy(struct domain *d)
>  {
> -    rxtx_unmap(d);
> +    rxtx_unmap(d, true /* teardown */);
>  }
>
>  void *ffa_rxtx_spmc_rx_acquire(void)
> --
> 2.52.0
>