On Fri, 2026-10-23 at 16:31 +0100, Peter Maydell wrote:
> The Cadence GEM allows the guest to program "screening registers"
> which control which queue received network data should be sent to.
> The field for the queue number in these registers is 4 bits, but the
> device might not actually have that many queues.  We weren't checking
> this, which meant the guest could program a value that caused us to
> index off the end of various arrays.
> 
> Check against num_priority_queues, and treat out-of-range values
> as if the screen didn't match (i.e. go to queue 0). (This is a
> best guess at reasonable behaviour since I don't have access to
> a datasheet for this device.)
> 
> Cc: [email protected]
> Fixes: e8e4994313b7 ("cadence_gem: Add support for screening")
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4363
> Signed-off-by: Peter Maydell <[email protected]>

Reviewed-by: Alistair Francis <[email protected]>

Alistair

> ---
>  hw/net/cadence_gem.c | 21 +++++++++++++++++----
>  1 file changed, 17 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
> index 39e3620ef5b..9a94b3b8ca4 100644
> --- a/hw/net/cadence_gem.c
> +++ b/hw/net/cadence_gem.c
> @@ -854,6 +854,7 @@ static int get_queue_from_screen(CadenceGEMState
> *s, uint8_t *rxbuf_ptr,
>      uint32_t reg;
>      bool matched, mismatched;
>      int i, j;
> +    int queue = 0; /* default to queue 0 if no match */
>  
>      for (i = 0; i < s->num_type1_screeners; i++) {
>          reg = s->regs[R_SCREENING_TYPE1_REG0 + i];
> @@ -881,7 +882,8 @@ static int get_queue_from_screen(CadenceGEMState
> *s, uint8_t *rxbuf_ptr,
>          }
>  
>          if (matched && !mismatched) {
> -            return FIELD_EX32(reg, SCREENING_TYPE1_REG0, QUEUE_NUM);
> +            queue = FIELD_EX32(reg, SCREENING_TYPE1_REG0,
> QUEUE_NUM);
> +            goto out;
>          }
>      }
>  
> @@ -974,12 +976,23 @@ static int
> get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr,
>          }
>  
>          if (matched && !mismatched) {
> -            return FIELD_EX32(reg, SCREENING_TYPE2_REG0, QUEUE_NUM);
> +            queue = FIELD_EX32(reg, SCREENING_TYPE2_REG0,
> QUEUE_NUM);
> +            break;
>          }
>      }
>  
> -    /* We made it here, assume it's queue 0 */
> -    return 0;
> +out:
> +    if (queue >= s->num_priority_queues) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "Screening register selected out of range
> queue %d\n",
> +                      queue);
> +        /*
> +         * This is a best guess at reasonable behaviour in the
> absence
> +         * of a datasheet to confirm it.
> +         */
> +        queue = 0;
> +    }
> +    return queue;
>  }
>  
>  static uint32_t gem_get_queue_base_addr(CadenceGEMState *s, bool tx,
> int q)

Reply via email to