Ilya Maximets <[email protected]> writes:

> On 3/25/25 16:38, Aaron Conole via dev wrote:
>> Daniel Ding <[email protected]> writes:
>> 
>>> From: Daniel Ding <[email protected]>
>>>
>>> In order to collect meters since the last meter action
>>> to the current action in the dpif_execute_helper_cb,
>>> the next entry is simply obtained using nl_attr_next, and
>>> without considering the nested nl. This will cause memory
>>> access to be overflowed.
>>>
>>> Gdb debug looks like this:
>>>
>>>   (gdb) print a
>>>   $6 = (const struct nlattr *) 0xaaaad175a69c
>>>   (gdb) n
>>>   1206                              a = nl_attr_next(a);
>>>   (gdb) n
>>>   1207                          } while (a != action &&
>>>   (gdb) n
>>>   1206                              a = nl_attr_next(a);
>>>   (gdb) print a
>>>   $7 = (const struct nlattr *) 0xaaaad175a69c
>>>   (gdb) print *a
>>>   $8 = {nla_len = 0, nla_type = 0}
>>>   (gdb)
>>>
>>> Fixes: 076caa2fb077 ("ofproto: Meter translation.")
>>> Signed-off-by: Daniel Ding <[email protected]>
>>> ---
>>>  lib/dpif.c | 43 ++++++++++++++++---------------------------
>>>  1 file changed, 16 insertions(+), 27 deletions(-)
>>>
>>> diff --git a/lib/dpif.c b/lib/dpif.c
>>> index d07241f1e..05dfd2919 100644
>>> --- a/lib/dpif.c
>>> +++ b/lib/dpif.c
>>> @@ -1158,11 +1158,14 @@ dpif_flow_dump_next(struct dpif_flow_dump_thread 
>>> *thread,
>>>      return n;
>>>  }
>>>  
>>> +enum { EXECUTE_HELPER_MAX_METER = 32 };
>>> +
>> 
>> Could we also prevent this with something like:
>> 
>> while (a != action && nl_attr_is_valid(a) &&
>>        nl_attr_type(a) != OVS_ACTION_ATTR_METER)
>
> I think nl_attr_is_valid(a) may still be accessing the memory beyond 
> allocated.
> If we want to fix the iteration this way, we should just use NL_ATTR_FOR_EACH
> and break at the right time.  It will perform all the necessary length checks.
>
> However, as rightly pointed out, this code can't handle nested attributes and
> so it's not correct in general.

It might be possible to rework this so that we have something like 
(psuedo-code):

bool nla_is_nested(const struct nlattr *nla) {
    return nla->nla_type & NLA_F_NESTED;
}

int validate_and_assist(actions, len) {
    int ret = 0, left;
    struct nlattr *a;

    NL_ATTR_FOR_EACH (a, left, actions, len) {
        if (ret) break;
        if (nla_is_nested(a)) {
            ret = validate_and_assist(a, a->nla_len);
            continue;
        }
        ... normal assist ...
    }
    return ret;
}

Then we handle nested attributes, and I think it is a bit easier to
read.

Maybe we could instead of doing recursively, do it as iterative by
breaking things up a bit differently:

bool nla_is_nested(const struct nlattr *nla) {
    return nla->nla_type & NLA_F_NESTED;
}

int assist_attr(attr) {
    ... normal assist ...
}

int assist_actions(actions, len) {
    int ret = 0, left;
    struct nlattr *a;

    NL_ATTR_FOR_EACH (a, left, actions, len) {
        if (nla_is_nested(a)) {
            struct nlattr *b;
            int bleft;
            NL_NESTED_FOR_EACH(b, bleft, a) {
                assist_attr(b);
            }
        } else {
            assist_attr(b);
        }
    }
}

Something like an above set of approachs at least will let us handle
nested attributes and could be a bit more pleasant to read.

>> 
>> I haven't done a deep review, just wondering because from a quick
>> glance, it seems that this change technically imposes limits on the
>> action list we are helping with, and then silently drops them after
>> that.
>
> It may be fine to limit the number of meters we will execute for one packet,
> 32 meters is kind of a lot for one packet.  This execution path is not great
> to begin with.  But yes, we could have a growing dynamic array or use a list
> instead to avoid missing some of the meters.
>
> A bigger problem here, however, is that we're accumulating those meters,
> but we're not removing them when we go back from the recursive execution of
> the clone or other nested action, so meters accumulated for one branch of
> execution will also be executed for the other branch, e.g. in
>    actions:clone(meter(1),<action-with-help>),meter(2),<action-with-help>
> In this scenario we will accumulate and execute both meters for the last
> action, while only the second meter should actually be executed.
>
>> 
>> Also, seems you could include a test case with the fix.
>
> Yes, we should have a test case for this issue.
>
> Best regards, Ilya Maximets.

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to