diff mbox series

[ovs-dev,v3,01/17] util: add multi-variable loop iterator macros

Message ID 20220216142800.3295236-2-amorenoz@redhat.com
State Superseded
Headers show
Series Fix undefined behavior in loop macros | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Adrián Moreno Feb. 16, 2022, 2:27 p.m. UTC
Multi-variable loop iterators avoid potential undefined behavior by
using an internal iterator variable to perform the iteration and only
referencing the containing object (via OBJECT_CONTAINING) if the
iterator has been validated via the second expression of the for
statement.

That way, the user can easily implement a loop that never tries to
obtain the object containing NULL or stack-allocated non-contained
nodes.

When the loop ends normally (not via "break;") the user-provided
variable is set to NULL.

Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
---
 include/openvswitch/util.h | 44 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

Comments

Dumitru Ceara Feb. 25, 2022, 12:06 p.m. UTC | #1
On 2/16/22 15:27, Adrian Moreno wrote:
> Multi-variable loop iterators avoid potential undefined behavior by
> using an internal iterator variable to perform the iteration and only
> referencing the containing object (via OBJECT_CONTAINING) if the
> iterator has been validated via the second expression of the for
> statement.
> 
> That way, the user can easily implement a loop that never tries to
> obtain the object containing NULL or stack-allocated non-contained
> nodes.
> 
> When the loop ends normally (not via "break;") the user-provided
> variable is set to NULL.
> 
> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
> ---

Hi Adrian,

It feels a bit weird that these new macros are not used anywhere yet
(until we apply the next patches in the series).  On the other hand I
don't think it's easy to split the series otherwise so, with a small nit
below:

Acked-by: Dumitru Ceara <dceara@redhat.com>

Thanks,
Dumitru

>  include/openvswitch/util.h | 44 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 44 insertions(+)
> 
> diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
> index 228b185c3..9c09e8aea 100644
> --- a/include/openvswitch/util.h
> +++ b/include/openvswitch/util.h
> @@ -145,6 +145,50 @@ OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *);
>  #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
>      ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
>  
> +

Nit: We don't need this extra line.

> +/* Multi-variable container iterators.
> + *
> + * The following macros facilitate safe iteration over data structures
> + * contained in objects. It does so by using an internal iterator variable of
> + * the type of the member object pointer (i.e: pointer to the data structure).
> + */
> +
> +/* Multi-variable iterator variable name.
> + * Returns the name of the internal iterator variable.
> + */
> +#define ITER_VAR(NAME) NAME ## __iterator__
> +
> +/* Multi-variable initialization. Creates an internal iterator variable that
> + * points to the provided pointer. The type of the iterator variable is
> + * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
> + *
> + * The _EXP version evaluates the extra expressions once.
> + */
> +#define INIT_MULTIVAR(VAR, MEMBER, POINTER, ITER_TYPE)                  \
> +    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
> +
> +#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...)         \
> +    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
> +
> +/* Multi-variable condition.
> + * Evaluates the condition expression (that must be based on the internal
> + * iterator variable). Only if the result of expression is true, the OBJECT is
> + * set to the object containing the current value of the iterator variable.
> + *
> + * It is up to the caller to make sure it is safe to run OBJECT_CONTAINING on
> + * the pointers that verify the condition.
> + */
> +#define CONDITION_MULTIVAR(VAR, MEMBER, EXPR)                                 \
> +    ((EXPR) ?                                                                 \
> +     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1) :           \
> +     (((VAR) = NULL), 0))
> +
> +/* Multi-variable update.
> + * Evaluates the expresssion that is supposed to update the iterator variable.
> + */
> +#define UPDATE_MULTIVAR(VAR, EXPR)                                            \
> +    ((EXPR), (VAR) = NULL)
> +
>  /* Returns the number of elements in ARRAY. */
>  #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY)
>
Adrián Moreno March 3, 2022, 6:43 a.m. UTC | #2
Thanks for the review Dumitru,

A comment below.

On 2/25/22 13:06, Dumitru Ceara wrote:
> On 2/16/22 15:27, Adrian Moreno wrote:
>> Multi-variable loop iterators avoid potential undefined behavior by
>> using an internal iterator variable to perform the iteration and only
>> referencing the containing object (via OBJECT_CONTAINING) if the
>> iterator has been validated via the second expression of the for
>> statement.
>>
>> That way, the user can easily implement a loop that never tries to
>> obtain the object containing NULL or stack-allocated non-contained
>> nodes.
>>
>> When the loop ends normally (not via "break;") the user-provided
>> variable is set to NULL.
>>
>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>> ---
> 
> Hi Adrian,
> 
> It feels a bit weird that these new macros are not used anywhere yet
> (until we apply the next patches in the series).  On the other hand I
> don't think it's easy to split the series otherwise so, with a small nit
> below:
> 
> Acked-by: Dumitru Ceara <dceara@redhat.com>
> 
> Thanks,
> Dumitru
> 
>>   include/openvswitch/util.h | 44 ++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 44 insertions(+)
>>
>> diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
>> index 228b185c3..9c09e8aea 100644
>> --- a/include/openvswitch/util.h
>> +++ b/include/openvswitch/util.h
>> @@ -145,6 +145,50 @@ OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *);
>>   #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
>>       ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
>>   
>> +
> 
> Nit: We don't need this extra line.
> 
>> +/* Multi-variable container iterators.
>> + *
>> + * The following macros facilitate safe iteration over data structures
>> + * contained in objects. It does so by using an internal iterator variable of
>> + * the type of the member object pointer (i.e: pointer to the data structure).
>> + */
>> +
>> +/* Multi-variable iterator variable name.
>> + * Returns the name of the internal iterator variable.
>> + */
>> +#define ITER_VAR(NAME) NAME ## __iterator__
>> +
>> +/* Multi-variable initialization. Creates an internal iterator variable that
>> + * points to the provided pointer. The type of the iterator variable is
>> + * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
>> + *
>> + * The _EXP version evaluates the extra expressions once.
>> + */
>> +#define INIT_MULTIVAR(VAR, MEMBER, POINTER, ITER_TYPE)                  \
>> +    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
>> +
>> +#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...)         \
>> +    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
>> +
>> +/* Multi-variable condition.
>> + * Evaluates the condition expression (that must be based on the internal
>> + * iterator variable). Only if the result of expression is true, the OBJECT is
>> + * set to the object containing the current value of the iterator variable.
>> + *
>> + * It is up to the caller to make sure it is safe to run OBJECT_CONTAINING on
>> + * the pointers that verify the condition.
>> + */
>> +#define CONDITION_MULTIVAR(VAR, MEMBER, EXPR)                                 \
>> +    ((EXPR) ?                                                                 \
>> +     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1) :           \
>> +     (((VAR) = NULL), 0))
>> +
>> +/* Multi-variable update.
>> + * Evaluates the expresssion that is supposed to update the iterator variable.
>> + */
>> +#define UPDATE_MULTIVAR(VAR, EXPR)                                            \
>> +    ((EXPR), (VAR) = NULL)
>> +

Setting VAR = NULL here may not really be needed. The CONDITION is evaluated 
just after the UPDATE stage and it sets VAR appropriately.

If you agree, I'll remove the entire UPDATE macro alongside the extra line and 
the extra "s" in "expression" in the next version.


>>   /* Returns the number of elements in ARRAY. */
>>   #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY)
>>   
>
Dumitru Ceara March 3, 2022, 9:39 a.m. UTC | #3
On 3/3/22 07:43, Adrian Moreno wrote:
> Thanks for the review Dumitru,
> 
> A comment below.
> 
> On 2/25/22 13:06, Dumitru Ceara wrote:
>> On 2/16/22 15:27, Adrian Moreno wrote:
>>> Multi-variable loop iterators avoid potential undefined behavior by
>>> using an internal iterator variable to perform the iteration and only
>>> referencing the containing object (via OBJECT_CONTAINING) if the
>>> iterator has been validated via the second expression of the for
>>> statement.
>>>
>>> That way, the user can easily implement a loop that never tries to
>>> obtain the object containing NULL or stack-allocated non-contained
>>> nodes.
>>>
>>> When the loop ends normally (not via "break;") the user-provided
>>> variable is set to NULL.
>>>
>>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>>> ---
>>
>> Hi Adrian,
>>
>> It feels a bit weird that these new macros are not used anywhere yet
>> (until we apply the next patches in the series).  On the other hand I
>> don't think it's easy to split the series otherwise so, with a small nit
>> below:
>>
>> Acked-by: Dumitru Ceara <dceara@redhat.com>
>>
>> Thanks,
>> Dumitru
>>
>>>   include/openvswitch/util.h | 44 ++++++++++++++++++++++++++++++++++++++
>>>   1 file changed, 44 insertions(+)
>>>
>>> diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
>>> index 228b185c3..9c09e8aea 100644
>>> --- a/include/openvswitch/util.h
>>> +++ b/include/openvswitch/util.h
>>> @@ -145,6 +145,50 @@ OVS_NO_RETURN void ovs_assert_failure(const char
>>> *, const char *, const char *);
>>>   #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
>>>       ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
>>>   +
>>
>> Nit: We don't need this extra line.
>>
>>> +/* Multi-variable container iterators.
>>> + *
>>> + * The following macros facilitate safe iteration over data structures
>>> + * contained in objects. It does so by using an internal iterator
>>> variable of
>>> + * the type of the member object pointer (i.e: pointer to the data
>>> structure).
>>> + */
>>> +
>>> +/* Multi-variable iterator variable name.
>>> + * Returns the name of the internal iterator variable.
>>> + */
>>> +#define ITER_VAR(NAME) NAME ## __iterator__
>>> +
>>> +/* Multi-variable initialization. Creates an internal iterator
>>> variable that
>>> + * points to the provided pointer. The type of the iterator variable is
>>> + * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
>>> + *
>>> + * The _EXP version evaluates the extra expressions once.
>>> + */
>>> +#define INIT_MULTIVAR(VAR, MEMBER, POINTER,
>>> ITER_TYPE)                  \
>>> +    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
>>> +
>>> +#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE,
>>> ...)         \
>>> +    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
>>> +
>>> +/* Multi-variable condition.
>>> + * Evaluates the condition expression (that must be based on the
>>> internal
>>> + * iterator variable). Only if the result of expression is true, the
>>> OBJECT is
>>> + * set to the object containing the current value of the iterator
>>> variable.
>>> + *
>>> + * It is up to the caller to make sure it is safe to run
>>> OBJECT_CONTAINING on
>>> + * the pointers that verify the condition.
>>> + */
>>> +#define CONDITION_MULTIVAR(VAR, MEMBER,
>>> EXPR)                                 \
>>> +    ((EXPR)
>>> ?                                                                 \
>>> +     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1)
>>> :           \
>>> +     (((VAR) = NULL), 0))
>>> +
>>> +/* Multi-variable update.
>>> + * Evaluates the expresssion that is supposed to update the iterator
>>> variable.
>>> + */
>>> +#define UPDATE_MULTIVAR(VAR,
>>> EXPR)                                            \
>>> +    ((EXPR), (VAR) = NULL)
>>> +
> 
> Setting VAR = NULL here may not really be needed. The CONDITION is
> evaluated just after the UPDATE stage and it sets VAR appropriately.
> 

Good point.  The same is true for the _SAFE_* versions in the next patch
too, right?

> If you agree, I'll remove the entire UPDATE macro alongside the extra
> line and the extra "s" in "expression" in the next version.
> 

Sounds good to me, thanks!
Adrián Moreno March 3, 2022, 10:45 a.m. UTC | #4
On 3/3/22 10:39, Dumitru Ceara wrote:
> On 3/3/22 07:43, Adrian Moreno wrote:
>> Thanks for the review Dumitru,
>>
>> A comment below.
>>
>> On 2/25/22 13:06, Dumitru Ceara wrote:
>>> On 2/16/22 15:27, Adrian Moreno wrote:
>>>> Multi-variable loop iterators avoid potential undefined behavior by
>>>> using an internal iterator variable to perform the iteration and only
>>>> referencing the containing object (via OBJECT_CONTAINING) if the
>>>> iterator has been validated via the second expression of the for
>>>> statement.
>>>>
>>>> That way, the user can easily implement a loop that never tries to
>>>> obtain the object containing NULL or stack-allocated non-contained
>>>> nodes.
>>>>
>>>> When the loop ends normally (not via "break;") the user-provided
>>>> variable is set to NULL.
>>>>
>>>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>>>> ---
>>>
>>> Hi Adrian,
>>>
>>> It feels a bit weird that these new macros are not used anywhere yet
>>> (until we apply the next patches in the series).  On the other hand I
>>> don't think it's easy to split the series otherwise so, with a small nit
>>> below:
>>>
>>> Acked-by: Dumitru Ceara <dceara@redhat.com>
>>>
>>> Thanks,
>>> Dumitru
>>>
>>>>    include/openvswitch/util.h | 44 ++++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 44 insertions(+)
>>>>
>>>> diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
>>>> index 228b185c3..9c09e8aea 100644
>>>> --- a/include/openvswitch/util.h
>>>> +++ b/include/openvswitch/util.h
>>>> @@ -145,6 +145,50 @@ OVS_NO_RETURN void ovs_assert_failure(const char
>>>> *, const char *, const char *);
>>>>    #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
>>>>        ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
>>>>    +
>>>
>>> Nit: We don't need this extra line.
>>>
>>>> +/* Multi-variable container iterators.
>>>> + *
>>>> + * The following macros facilitate safe iteration over data structures
>>>> + * contained in objects. It does so by using an internal iterator
>>>> variable of
>>>> + * the type of the member object pointer (i.e: pointer to the data
>>>> structure).
>>>> + */
>>>> +
>>>> +/* Multi-variable iterator variable name.
>>>> + * Returns the name of the internal iterator variable.
>>>> + */
>>>> +#define ITER_VAR(NAME) NAME ## __iterator__
>>>> +
>>>> +/* Multi-variable initialization. Creates an internal iterator
>>>> variable that
>>>> + * points to the provided pointer. The type of the iterator variable is
>>>> + * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
>>>> + *
>>>> + * The _EXP version evaluates the extra expressions once.
>>>> + */
>>>> +#define INIT_MULTIVAR(VAR, MEMBER, POINTER,
>>>> ITER_TYPE)                  \
>>>> +    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
>>>> +
>>>> +#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE,
>>>> ...)         \
>>>> +    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
>>>> +
>>>> +/* Multi-variable condition.
>>>> + * Evaluates the condition expression (that must be based on the
>>>> internal
>>>> + * iterator variable). Only if the result of expression is true, the
>>>> OBJECT is
>>>> + * set to the object containing the current value of the iterator
>>>> variable.
>>>> + *
>>>> + * It is up to the caller to make sure it is safe to run
>>>> OBJECT_CONTAINING on
>>>> + * the pointers that verify the condition.
>>>> + */
>>>> +#define CONDITION_MULTIVAR(VAR, MEMBER,
>>>> EXPR)                                 \
>>>> +    ((EXPR)
>>>> ?                                                                 \
>>>> +     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1)
>>>> :           \
>>>> +     (((VAR) = NULL), 0))
>>>> +
>>>> +/* Multi-variable update.
>>>> + * Evaluates the expresssion that is supposed to update the iterator
>>>> variable.
>>>> + */
>>>> +#define UPDATE_MULTIVAR(VAR,
>>>> EXPR)                                            \
>>>> +    ((EXPR), (VAR) = NULL)
>>>> +
>>
>> Setting VAR = NULL here may not really be needed. The CONDITION is
>> evaluated just after the UPDATE stage and it sets VAR appropriately.
>>
> 
> Good point.  The same is true for the _SAFE_* versions in the next patch
> too, right?
> 

Yes.

>> If you agree, I'll remove the entire UPDATE macro alongside the extra
>> line and the extra "s" in "expression" in the next version.
>>
> 
> Sounds good to me, thanks!
> 

Ack, thanks.
diff mbox series

Patch

diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
index 228b185c3..9c09e8aea 100644
--- a/include/openvswitch/util.h
+++ b/include/openvswitch/util.h
@@ -145,6 +145,50 @@  OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *);
 #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
     ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
 
+
+/* Multi-variable container iterators.
+ *
+ * The following macros facilitate safe iteration over data structures
+ * contained in objects. It does so by using an internal iterator variable of
+ * the type of the member object pointer (i.e: pointer to the data structure).
+ */
+
+/* Multi-variable iterator variable name.
+ * Returns the name of the internal iterator variable.
+ */
+#define ITER_VAR(NAME) NAME ## __iterator__
+
+/* Multi-variable initialization. Creates an internal iterator variable that
+ * points to the provided pointer. The type of the iterator variable is
+ * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
+ *
+ * The _EXP version evaluates the extra expressions once.
+ */
+#define INIT_MULTIVAR(VAR, MEMBER, POINTER, ITER_TYPE)                  \
+    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
+
+#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...)         \
+    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
+
+/* Multi-variable condition.
+ * Evaluates the condition expression (that must be based on the internal
+ * iterator variable). Only if the result of expression is true, the OBJECT is
+ * set to the object containing the current value of the iterator variable.
+ *
+ * It is up to the caller to make sure it is safe to run OBJECT_CONTAINING on
+ * the pointers that verify the condition.
+ */
+#define CONDITION_MULTIVAR(VAR, MEMBER, EXPR)                                 \
+    ((EXPR) ?                                                                 \
+     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1) :           \
+     (((VAR) = NULL), 0))
+
+/* Multi-variable update.
+ * Evaluates the expresssion that is supposed to update the iterator variable.
+ */
+#define UPDATE_MULTIVAR(VAR, EXPR)                                            \
+    ((EXPR), (VAR) = NULL)
+
 /* Returns the number of elements in ARRAY. */
 #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY)