[PATCH] Don't deref the sample pointer in the wl_container_of macro

Kristian Høgsberg hoegsberg at gmail.com
Wed Feb 5 17:22:50 PST 2014


On Tue, Feb 04, 2014 at 02:21:48PM +0000, Neil Roberts wrote:
> The previous implementation of the wl_container_of macro was
> dereferencing the sample pointer in order to get an address of the
> member to calculate the offset. Ideally this shouldn't cause any
> problems because the dereference doesn't actually cause the address to
> be read from so it shouldn't matter if the pointer is uninitialised.
> However this is probably technically invalid and could cause undefined
> behavior. Clang appears to take advantage of this undefined behavior
> and doesn't bother doing the subtraction. It also gives a warning when
> it does this.
> 
> The documentation for wl_container_of implies that it should only be
> given an initialised pointer and if that is done then there is no
> problem with clang. However this is quite easy to forget and doesn't
> cause any problems or warnings with gcc so it's quite easy to
> accidentally break clang.
> 
> To fix the problem this changes the macro to use pointer -
> offsetof(__typeof__(sample), member) so that it doesn't need to deref
> the sample pointer. This does however require that the __typeof__
> operator is supported by the compiler. In practice we probably only
> care about gcc and clang and both of these happily support the
> operator.
> 
> The previous implementation was also using __typeof__ but it had a
> fallback path avoiding it when the operator isn't available. The
> fallback effectively has undefined behaviour and it is targetting
> unknown compilers so it is probably not a good idea to leave it in.
> Instead, this patch just removes it. If someone finds a compiler that
> doesn't have __typeof__ but does work with the old implementation then
> maybe they could add it back in as a special case.
> 
> This patch removes the initialisation anywhere where the sample
> pointer was being unitialised before using wl_container_of. The
> documentation for the macro has also been updated to specify that this
> is OK.

Thanks, that's nice.  Patch applied.

Kristian
> ---
>  src/wayland-server.c |  2 +-
>  src/wayland-server.h |  2 +-
>  src/wayland-util.h   | 30 +++++++++++++-----------------
>  3 files changed, 15 insertions(+), 19 deletions(-)
> 
> diff --git a/src/wayland-server.c b/src/wayland-server.c
> index 489b99d..294497d 100644
> --- a/src/wayland-server.c
> +++ b/src/wayland-server.c
> @@ -571,7 +571,7 @@ wl_resource_get_link(struct wl_resource *resource)
>  WL_EXPORT struct wl_resource *
>  wl_resource_from_link(struct wl_list *link)
>  {
> -	struct wl_resource *resource = NULL;
> +	struct wl_resource *resource;
>  
>  	return wl_container_of(link, resource, link);
>  }
> diff --git a/src/wayland-server.h b/src/wayland-server.h
> index 3fcaaf6..7fc5b47 100644
> --- a/src/wayland-server.h
> +++ b/src/wayland-server.h
> @@ -162,7 +162,7 @@ wl_client_post_no_memory(struct wl_client *client);
>   * \code
>   * void your_listener(struct wl_listener *listener, void *data)
>   * {
> - * 	struct your_data *data = NULL;
> + * 	struct your_data *data;
>   *
>   * 	your_data = wl_container_of(listener, data, your_member_name);
>   * }
> diff --git a/src/wayland-util.h b/src/wayland-util.h
> index 68d91e2..57764d9 100644
> --- a/src/wayland-util.h
> +++ b/src/wayland-util.h
> @@ -135,7 +135,7 @@ void wl_list_insert_list(struct wl_list *list, struct wl_list *other);
>   *
>   * void example_container_destroy(struct wl_listener *listener, void *data)
>   * {
> - *     struct example_container *ctr = NULL;
> + *     struct example_container *ctr;
>   *
>   *     ctr = wl_container_of(listener, ctr, destroy_listener);
>   *     \comment{destroy ctr...}
> @@ -144,44 +144,40 @@ void wl_list_insert_list(struct wl_list *list, struct wl_list *other);
>   *
>   * \param ptr A valid pointer to the contained item.
>   *
> - * \param sample A pointer to the type of content that the list item stores.
> - * Sample does not need be a valid pointer; a null pointer will suffice.
> + * \param sample A pointer to the type of content that the list item
> + * stores. Sample does not need be a valid pointer; a null or
> + * an uninitialised pointer will suffice.
>   *
>   * \param member The named location of ptr within the sample type.
>   *
>   * \return The container for the specified pointer.
>   */
> -#ifdef __GNUC__
>  #define wl_container_of(ptr, sample, member)				\
> -	(__typeof__(sample))((char *)(ptr)	-			\
> -		 ((char *)&(sample)->member - (char *)(sample)))
> -#else
> -#define wl_container_of(ptr, sample, member)				\
> -	(void *)((char *)(ptr)	-				        \
> -		 ((char *)&(sample)->member - (char *)(sample)))
> -#endif
> +	(__typeof__(sample))((char *)(ptr) -				\
> +			     offsetof(__typeof__(*sample), member))
> +/* If the above macro causes problems on your compiler you might be
> + * able to find an alternative name for the non-standard __typeof__
> + * operator and add a special case here */
>  
>  #define wl_list_for_each(pos, head, member)				\
> -	for (pos = 0, pos = wl_container_of((head)->next, pos, member);	\
> +	for (pos = wl_container_of((head)->next, pos, member);	\
>  	     &pos->member != (head);					\
>  	     pos = wl_container_of(pos->member.next, pos, member))
>  
>  #define wl_list_for_each_safe(pos, tmp, head, member)			\
> -	for (pos = 0, tmp = 0, 						\
> -	     pos = wl_container_of((head)->next, pos, member),		\
> +	for (pos = wl_container_of((head)->next, pos, member),		\
>  	     tmp = wl_container_of((pos)->member.next, tmp, member);	\
>  	     &pos->member != (head);					\
>  	     pos = tmp,							\
>  	     tmp = wl_container_of(pos->member.next, tmp, member))
>  
>  #define wl_list_for_each_reverse(pos, head, member)			\
> -	for (pos = 0, pos = wl_container_of((head)->prev, pos, member);	\
> +	for (pos = wl_container_of((head)->prev, pos, member);	\
>  	     &pos->member != (head);					\
>  	     pos = wl_container_of(pos->member.prev, pos, member))
>  
>  #define wl_list_for_each_reverse_safe(pos, tmp, head, member)		\
> -	for (pos = 0, tmp = 0, 						\
> -	     pos = wl_container_of((head)->prev, pos, member),	\
> +	for (pos = wl_container_of((head)->prev, pos, member),	\
>  	     tmp = wl_container_of((pos)->member.prev, tmp, member);	\
>  	     &pos->member != (head);					\
>  	     pos = tmp,							\
> -- 
> 1.8.4.2
> 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list