netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* xdp/xsk.c: Possible bug in xdp_umem_reg version check
@ 2024-07-07 15:05 Julian Schindel
  2024-07-09  9:23 ` Magnus Karlsson
  0 siblings, 1 reply; 8+ messages in thread
From: Julian Schindel @ 2024-07-07 15:05 UTC (permalink / raw)
  To: bpf
  Cc: Björn Töpel, Magnus Karlsson, Maciej Fijalkowski,
	Stanislav Fomichev, netdev

Hi,

I hope this is the correct way to ask about this issue, I haven't used
the kernel mailing list before.

Between different compilations of an AF_XDP project, I encountered
"random" EINVAL errors when calling setsockopt XDP_UMEM_REG with the
same parameter.

I think this might be caused by this patch:
https://lore.kernel.org/all/20231127190319.1190813-2-sdf@google.com/
It added "tx_metadata_len" to the "xdp_umem_reg" struct.
In the  "xsk_setsockopt" code in xdp/xsk.c, the provided "optlen" is
checked against the length of "xdp_umem_reg_v2" and "xdp_umem_reg" to
check which version of "xdp_umem_reg", the user supplied.

At least on my machine (x86_64, Fedora 40, 6.9.7), these two structs
have the same size (32 bytes) due to the compiler adding padding to
"xdp_umem_reg_v2". This means if the user supplies "xdp_umem_reg_v2", it
is falsely treated as "xdp_umem_reg".

I'm not sure whether there is some implicit struct packing happening or
whether this is indeed a bug.

Best regards,
Julian


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-07 15:05 xdp/xsk.c: Possible bug in xdp_umem_reg version check Julian Schindel
@ 2024-07-09  9:23 ` Magnus Karlsson
  2024-07-09 11:25   ` Julian Schindel
  0 siblings, 1 reply; 8+ messages in thread
From: Magnus Karlsson @ 2024-07-09  9:23 UTC (permalink / raw)
  To: Julian Schindel
  Cc: bpf, Björn Töpel, Magnus Karlsson, Maciej Fijalkowski,
	Stanislav Fomichev, netdev

On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
>
> Hi,
>
> I hope this is the correct way to ask about this issue, I haven't used
> the kernel mailing list before.
>
> Between different compilations of an AF_XDP project, I encountered
> "random" EINVAL errors when calling setsockopt XDP_UMEM_REG with the
> same parameter.
>
> I think this might be caused by this patch:
> https://lore.kernel.org/all/20231127190319.1190813-2-sdf@google.com/
> It added "tx_metadata_len" to the "xdp_umem_reg" struct.
> In the  "xsk_setsockopt" code in xdp/xsk.c, the provided "optlen" is
> checked against the length of "xdp_umem_reg_v2" and "xdp_umem_reg" to
> check which version of "xdp_umem_reg", the user supplied.
>
> At least on my machine (x86_64, Fedora 40, 6.9.7), these two structs
> have the same size (32 bytes) due to the compiler adding padding to
> "xdp_umem_reg_v2". This means if the user supplies "xdp_umem_reg_v2", it
> is falsely treated as "xdp_umem_reg".
>
> I'm not sure whether there is some implicit struct packing happening or
> whether this is indeed a bug.

Thank you for reporting this Julian. This seems to be a bug. If I
check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
on my system, compiling with gcc 11.4. I am not a compiler guy so do
not know what the rules are for padding structs, but I read the
following from [0]:

"Pad the entire struct to a multiple of 64-bits if the structure
contains 64-bit types - the structure size will otherwise differ on
32-bit versus 64-bit. Having a different structure size hurts when
passing arrays of structures to the kernel, or if the kernel checks
the structure size, which e.g. the drm core does."

I compiled for 64-bits and I believe you did too, but we still get
this padding. What is sizeof(struct xdp_umem_reg) for you before the
patch that added tx_metadata_len?

[0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html

> Best regards,
> Julian
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-09  9:23 ` Magnus Karlsson
@ 2024-07-09 11:25   ` Julian Schindel
  2024-07-10  4:45     ` Stanislav Fomichev
  0 siblings, 1 reply; 8+ messages in thread
From: Julian Schindel @ 2024-07-09 11:25 UTC (permalink / raw)
  To: Magnus Karlsson
  Cc: bpf, Björn Töpel, Magnus Karlsson, Maciej Fijalkowski,
	Stanislav Fomichev, netdev

On 09.07.24 11:23, Magnus Karlsson wrote:
> On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
>> Hi,
>>
>> [...]
> Thank you for reporting this Julian. This seems to be a bug. If I
> check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
> on my system, compiling with gcc 11.4. I am not a compiler guy so do
> not know what the rules are for padding structs, but I read the
> following from [0]:
>
> "Pad the entire struct to a multiple of 64-bits if the structure
> contains 64-bit types - the structure size will otherwise differ on
> 32-bit versus 64-bit. Having a different structure size hurts when
> passing arrays of structures to the kernel, or if the kernel checks
> the structure size, which e.g. the drm core does."
>
> I compiled for 64-bits and I believe you did too, but we still get
> this padding. 
Yes, I did also compile for 64-bits. If I understood the resource you
linked correctly, the compiler automatically adding padding to align to
64-bit boundaries is expected for 64-bit platforms:

"[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
boundaries, but 64-bit platforms do. So we always need padding to the
natural size to get this right."
> What is sizeof(struct xdp_umem_reg) for you before the
> patch that added tx_metadata_len?
I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
after the patch. I'm not sure how to check this with different kernel
versions.

Maybe the following code helps show all the sizes
of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
test" using gcc 14.1.1):

#include <stdio.h>
#include <sys/types.h>

typedef __uint32_t __u32;
typedef __uint64_t __u64;

struct xdp_umem_reg_v1  {
    __u64 addr; /* Start of packet data area */
    __u64 len; /* Length of packet data area */
    __u32 chunk_size;
    __u32 headroom;
};

struct xdp_umem_reg_v2 {
    __u64 addr; /* Start of packet data area */
    __u64 len; /* Length of packet data area */
    __u32 chunk_size;
    __u32 headroom;
    __u32 flags;
};

struct xdp_umem_reg {
    __u64 addr; /* Start of packet data area */
    __u64 len; /* Length of packet data area */
    __u32 chunk_size;
    __u32 headroom;
    __u32 flags;
    __u32 tx_metadata_len;
};

int main() {
    printf("__u32: \t\t\t %lu\n", sizeof(__u32));
    printf("__u64: \t\t\t %lu\n", sizeof(__u64));
    printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
    printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
    printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
}

Running "./test" produced this output:

__u32:                   4
__u64:                   8
xdp_umem_reg_v1:         24
xdp_umem_reg_v2:         32
xdp_umem_reg:            32
> [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html
>> Best regards,
>> Julian


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-09 11:25   ` Julian Schindel
@ 2024-07-10  4:45     ` Stanislav Fomichev
  2024-07-10  6:32       ` Julian Schindel
  0 siblings, 1 reply; 8+ messages in thread
From: Stanislav Fomichev @ 2024-07-10  4:45 UTC (permalink / raw)
  To: Julian Schindel
  Cc: Magnus Karlsson, bpf, Björn Töpel, Magnus Karlsson,
	Maciej Fijalkowski, Stanislav Fomichev, netdev

On 07/09, Julian Schindel wrote:
> On 09.07.24 11:23, Magnus Karlsson wrote:
> > On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
> >> Hi,
> >>
> >> [...]
> > Thank you for reporting this Julian. This seems to be a bug. If I
> > check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
> > on my system, compiling with gcc 11.4. I am not a compiler guy so do
> > not know what the rules are for padding structs, but I read the
> > following from [0]:
> >
> > "Pad the entire struct to a multiple of 64-bits if the structure
> > contains 64-bit types - the structure size will otherwise differ on
> > 32-bit versus 64-bit. Having a different structure size hurts when
> > passing arrays of structures to the kernel, or if the kernel checks
> > the structure size, which e.g. the drm core does."
> >
> > I compiled for 64-bits and I believe you did too, but we still get
> > this padding. 
> Yes, I did also compile for 64-bits. If I understood the resource you
> linked correctly, the compiler automatically adding padding to align to
> 64-bit boundaries is expected for 64-bit platforms:
> 
> "[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
> boundaries, but 64-bit platforms do. So we always need padding to the
> natural size to get this right."
> > What is sizeof(struct xdp_umem_reg) for you before the
> > patch that added tx_metadata_len?
> I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
> after the patch. I'm not sure how to check this with different kernel
> versions.
> 
> Maybe the following code helps show all the sizes
> of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
> test" using gcc 14.1.1):
> 
> #include <stdio.h>
> #include <sys/types.h>
> 
> typedef __uint32_t __u32;
> typedef __uint64_t __u64;
> 
> struct xdp_umem_reg_v1  {
>     __u64 addr; /* Start of packet data area */
>     __u64 len; /* Length of packet data area */
>     __u32 chunk_size;
>     __u32 headroom;
> };
> 
> struct xdp_umem_reg_v2 {
>     __u64 addr; /* Start of packet data area */
>     __u64 len; /* Length of packet data area */
>     __u32 chunk_size;
>     __u32 headroom;
>     __u32 flags;
> };
> 
> struct xdp_umem_reg {
>     __u64 addr; /* Start of packet data area */
>     __u64 len; /* Length of packet data area */
>     __u32 chunk_size;
>     __u32 headroom;
>     __u32 flags;
>     __u32 tx_metadata_len;
> };
> 
> int main() {
>     printf("__u32: \t\t\t %lu\n", sizeof(__u32));
>     printf("__u64: \t\t\t %lu\n", sizeof(__u64));
>     printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
>     printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
>     printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
> }
> 
> Running "./test" produced this output:
> 
> __u32:                   4
> __u64:                   8
> xdp_umem_reg_v1:         24
> xdp_umem_reg_v2:         32
> xdp_umem_reg:            32
> > [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html

Hmm, true, this means our version check won't really work :-/ I don't
see a good way to solve it without breaking the uapi. We can either
add some new padding field to xdp_umem_reg to make it larger than _v2.
Or we can add a new flag to signify the presence of tx_metadata_len
and do the validation based on that.

Btw, what are you using to setup umem? Looking at libxsk, it does
`memset(&mr, 0, sizeof(mr));` which should clear the padding as well.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-10  4:45     ` Stanislav Fomichev
@ 2024-07-10  6:32       ` Julian Schindel
  2024-07-11  3:48         ` Stanislav Fomichev
  0 siblings, 1 reply; 8+ messages in thread
From: Julian Schindel @ 2024-07-10  6:32 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Magnus Karlsson, bpf, Björn Töpel, Magnus Karlsson,
	Maciej Fijalkowski, Stanislav Fomichev, netdev

On 10.07.24 06:45, Stanislav Fomichev wrote:
> On 07/09, Julian Schindel wrote:
>> On 09.07.24 11:23, Magnus Karlsson wrote:
>>> On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
>>>> Hi,
>>>>
>>>> [...]
>>> Thank you for reporting this Julian. This seems to be a bug. If I
>>> check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
>>> on my system, compiling with gcc 11.4. I am not a compiler guy so do
>>> not know what the rules are for padding structs, but I read the
>>> following from [0]:
>>>
>>> "Pad the entire struct to a multiple of 64-bits if the structure
>>> contains 64-bit types - the structure size will otherwise differ on
>>> 32-bit versus 64-bit. Having a different structure size hurts when
>>> passing arrays of structures to the kernel, or if the kernel checks
>>> the structure size, which e.g. the drm core does."
>>>
>>> I compiled for 64-bits and I believe you did too, but we still get
>>> this padding. 
>> Yes, I did also compile for 64-bits. If I understood the resource you
>> linked correctly, the compiler automatically adding padding to align to
>> 64-bit boundaries is expected for 64-bit platforms:
>>
>> "[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
>> boundaries, but 64-bit platforms do. So we always need padding to the
>> natural size to get this right."
>>> What is sizeof(struct xdp_umem_reg) for you before the
>>> patch that added tx_metadata_len?
>> I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
>> after the patch. I'm not sure how to check this with different kernel
>> versions.
>>
>> Maybe the following code helps show all the sizes
>> of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
>> test" using gcc 14.1.1):
>>
>> #include <stdio.h>
>> #include <sys/types.h>
>>
>> typedef __uint32_t __u32;
>> typedef __uint64_t __u64;
>>
>> struct xdp_umem_reg_v1  {
>>     __u64 addr; /* Start of packet data area */
>>     __u64 len; /* Length of packet data area */
>>     __u32 chunk_size;
>>     __u32 headroom;
>> };
>>
>> struct xdp_umem_reg_v2 {
>>     __u64 addr; /* Start of packet data area */
>>     __u64 len; /* Length of packet data area */
>>     __u32 chunk_size;
>>     __u32 headroom;
>>     __u32 flags;
>> };
>>
>> struct xdp_umem_reg {
>>     __u64 addr; /* Start of packet data area */
>>     __u64 len; /* Length of packet data area */
>>     __u32 chunk_size;
>>     __u32 headroom;
>>     __u32 flags;
>>     __u32 tx_metadata_len;
>> };
>>
>> int main() {
>>     printf("__u32: \t\t\t %lu\n", sizeof(__u32));
>>     printf("__u64: \t\t\t %lu\n", sizeof(__u64));
>>     printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
>>     printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
>>     printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
>> }
>>
>> Running "./test" produced this output:
>>
>> __u32:                   4
>> __u64:                   8
>> xdp_umem_reg_v1:         24
>> xdp_umem_reg_v2:         32
>> xdp_umem_reg:            32
>>> [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html
> Hmm, true, this means our version check won't really work :-/ I don't
> see a good way to solve it without breaking the uapi. We can either
> add some new padding field to xdp_umem_reg to make it larger than _v2.
> Or we can add a new flag to signify the presence of tx_metadata_len
> and do the validation based on that.
>
> Btw, what are you using to setup umem? Looking at libxsk, it does
> `memset(&mr, 0, sizeof(mr));` which should clear the padding as well.

I'm using "setsockopt" directly with Rust bindings and the C
representation of Rust structs [1]. I'm guessing the compiler is not
zeroing the padding, which is why I encountered the issue.

[1]:
https://doc.rust-lang.org/reference/type-layout.html#the-c-representation

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-10  6:32       ` Julian Schindel
@ 2024-07-11  3:48         ` Stanislav Fomichev
  2024-07-11  5:23           ` Julian Schindel
  2024-07-11  8:11           ` Magnus Karlsson
  0 siblings, 2 replies; 8+ messages in thread
From: Stanislav Fomichev @ 2024-07-11  3:48 UTC (permalink / raw)
  To: Julian Schindel
  Cc: Magnus Karlsson, bpf, Björn Töpel, Magnus Karlsson,
	Maciej Fijalkowski, Stanislav Fomichev, netdev

On 07/10, Julian Schindel wrote:
> On 10.07.24 06:45, Stanislav Fomichev wrote:
> > On 07/09, Julian Schindel wrote:
> >> On 09.07.24 11:23, Magnus Karlsson wrote:
> >>> On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
> >>>> Hi,
> >>>>
> >>>> [...]
> >>> Thank you for reporting this Julian. This seems to be a bug. If I
> >>> check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
> >>> on my system, compiling with gcc 11.4. I am not a compiler guy so do
> >>> not know what the rules are for padding structs, but I read the
> >>> following from [0]:
> >>>
> >>> "Pad the entire struct to a multiple of 64-bits if the structure
> >>> contains 64-bit types - the structure size will otherwise differ on
> >>> 32-bit versus 64-bit. Having a different structure size hurts when
> >>> passing arrays of structures to the kernel, or if the kernel checks
> >>> the structure size, which e.g. the drm core does."
> >>>
> >>> I compiled for 64-bits and I believe you did too, but we still get
> >>> this padding. 
> >> Yes, I did also compile for 64-bits. If I understood the resource you
> >> linked correctly, the compiler automatically adding padding to align to
> >> 64-bit boundaries is expected for 64-bit platforms:
> >>
> >> "[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
> >> boundaries, but 64-bit platforms do. So we always need padding to the
> >> natural size to get this right."
> >>> What is sizeof(struct xdp_umem_reg) for you before the
> >>> patch that added tx_metadata_len?
> >> I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
> >> after the patch. I'm not sure how to check this with different kernel
> >> versions.
> >>
> >> Maybe the following code helps show all the sizes
> >> of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
> >> test" using gcc 14.1.1):
> >>
> >> #include <stdio.h>
> >> #include <sys/types.h>
> >>
> >> typedef __uint32_t __u32;
> >> typedef __uint64_t __u64;
> >>
> >> struct xdp_umem_reg_v1  {
> >>     __u64 addr; /* Start of packet data area */
> >>     __u64 len; /* Length of packet data area */
> >>     __u32 chunk_size;
> >>     __u32 headroom;
> >> };
> >>
> >> struct xdp_umem_reg_v2 {
> >>     __u64 addr; /* Start of packet data area */
> >>     __u64 len; /* Length of packet data area */
> >>     __u32 chunk_size;
> >>     __u32 headroom;
> >>     __u32 flags;
> >> };
> >>
> >> struct xdp_umem_reg {
> >>     __u64 addr; /* Start of packet data area */
> >>     __u64 len; /* Length of packet data area */
> >>     __u32 chunk_size;
> >>     __u32 headroom;
> >>     __u32 flags;
> >>     __u32 tx_metadata_len;
> >> };
> >>
> >> int main() {
> >>     printf("__u32: \t\t\t %lu\n", sizeof(__u32));
> >>     printf("__u64: \t\t\t %lu\n", sizeof(__u64));
> >>     printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
> >>     printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
> >>     printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
> >> }
> >>
> >> Running "./test" produced this output:
> >>
> >> __u32:                   4
> >> __u64:                   8
> >> xdp_umem_reg_v1:         24
> >> xdp_umem_reg_v2:         32
> >> xdp_umem_reg:            32
> >>> [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html
> > Hmm, true, this means our version check won't really work :-/ I don't
> > see a good way to solve it without breaking the uapi. We can either
> > add some new padding field to xdp_umem_reg to make it larger than _v2.
> > Or we can add a new flag to signify the presence of tx_metadata_len
> > and do the validation based on that.
> >
> > Btw, what are you using to setup umem? Looking at libxsk, it does
> > `memset(&mr, 0, sizeof(mr));` which should clear the padding as well.
> 
> I'm using "setsockopt" directly with Rust bindings and the C
> representation of Rust structs [1]. I'm guessing the compiler is not
> zeroing the padding, which is why I encountered the issue.
> 
> [1]:
> https://doc.rust-lang.org/reference/type-layout.html#the-c-representation

Awesome, thanks for confirming! I guess for now you can work it around
by having an explicit padding field and setting it to zero?

For a long-term fix, I'm leaning towards adding new umem flag as
a signal to the kernel to interpret this as a tx_metadata_len. But
this is gonna break any existing users that set this value. Hopefully
should not be a lot of them since it is a pretty recent functionality.

I'm also gonna sprinkle some compile time asserts to make sure we can extend
xdp_umem_reg in the future without hitting the same issue again. I'm a
bit spoiled by sys_bpf which takes care of enforcing the padding being
zero.

Magnus, any better ideas?

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-11  3:48         ` Stanislav Fomichev
@ 2024-07-11  5:23           ` Julian Schindel
  2024-07-11  8:11           ` Magnus Karlsson
  1 sibling, 0 replies; 8+ messages in thread
From: Julian Schindel @ 2024-07-11  5:23 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Magnus Karlsson, bpf, Björn Töpel, Magnus Karlsson,
	Maciej Fijalkowski, Stanislav Fomichev, netdev

On 11.07.24 05:48, Stanislav Fomichev wrote:
> On 07/10, Julian Schindel wrote:
>> On 10.07.24 06:45, Stanislav Fomichev wrote:
>>> On 07/09, Julian Schindel wrote:
>>>> On 09.07.24 11:23, Magnus Karlsson wrote:
>>>>> On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> [...]
>>>>> Thank you for reporting this Julian. This seems to be a bug. If I
>>>>> check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
>>>>> on my system, compiling with gcc 11.4. I am not a compiler guy so do
>>>>> not know what the rules are for padding structs, but I read the
>>>>> following from [0]:
>>>>>
>>>>> "Pad the entire struct to a multiple of 64-bits if the structure
>>>>> contains 64-bit types - the structure size will otherwise differ on
>>>>> 32-bit versus 64-bit. Having a different structure size hurts when
>>>>> passing arrays of structures to the kernel, or if the kernel checks
>>>>> the structure size, which e.g. the drm core does."
>>>>>
>>>>> I compiled for 64-bits and I believe you did too, but we still get
>>>>> this padding. 
>>>> Yes, I did also compile for 64-bits. If I understood the resource you
>>>> linked correctly, the compiler automatically adding padding to align to
>>>> 64-bit boundaries is expected for 64-bit platforms:
>>>>
>>>> "[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
>>>> boundaries, but 64-bit platforms do. So we always need padding to the
>>>> natural size to get this right."
>>>>> What is sizeof(struct xdp_umem_reg) for you before the
>>>>> patch that added tx_metadata_len?
>>>> I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
>>>> after the patch. I'm not sure how to check this with different kernel
>>>> versions.
>>>>
>>>> Maybe the following code helps show all the sizes
>>>> of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
>>>> test" using gcc 14.1.1):
>>>>
>>>> #include <stdio.h>
>>>> #include <sys/types.h>
>>>>
>>>> typedef __uint32_t __u32;
>>>> typedef __uint64_t __u64;
>>>>
>>>> struct xdp_umem_reg_v1  {
>>>>     __u64 addr; /* Start of packet data area */
>>>>     __u64 len; /* Length of packet data area */
>>>>     __u32 chunk_size;
>>>>     __u32 headroom;
>>>> };
>>>>
>>>> struct xdp_umem_reg_v2 {
>>>>     __u64 addr; /* Start of packet data area */
>>>>     __u64 len; /* Length of packet data area */
>>>>     __u32 chunk_size;
>>>>     __u32 headroom;
>>>>     __u32 flags;
>>>> };
>>>>
>>>> struct xdp_umem_reg {
>>>>     __u64 addr; /* Start of packet data area */
>>>>     __u64 len; /* Length of packet data area */
>>>>     __u32 chunk_size;
>>>>     __u32 headroom;
>>>>     __u32 flags;
>>>>     __u32 tx_metadata_len;
>>>> };
>>>>
>>>> int main() {
>>>>     printf("__u32: \t\t\t %lu\n", sizeof(__u32));
>>>>     printf("__u64: \t\t\t %lu\n", sizeof(__u64));
>>>>     printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
>>>>     printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
>>>>     printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
>>>> }
>>>>
>>>> Running "./test" produced this output:
>>>>
>>>> __u32:                   4
>>>> __u64:                   8
>>>> xdp_umem_reg_v1:         24
>>>> xdp_umem_reg_v2:         32
>>>> xdp_umem_reg:            32
>>>>> [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html
>>> Hmm, true, this means our version check won't really work :-/ I don't
>>> see a good way to solve it without breaking the uapi. We can either
>>> add some new padding field to xdp_umem_reg to make it larger than _v2.
>>> Or we can add a new flag to signify the presence of tx_metadata_len
>>> and do the validation based on that.
>>>
>>> Btw, what are you using to setup umem? Looking at libxsk, it does
>>> `memset(&mr, 0, sizeof(mr));` which should clear the padding as well.
>> I'm using "setsockopt" directly with Rust bindings and the C
>> representation of Rust structs [1]. I'm guessing the compiler is not
>> zeroing the padding, which is why I encountered the issue.
>>
>> [1]:
>> https://doc.rust-lang.org/reference/type-layout.html#the-c-representation
> Awesome, thanks for confirming! I guess for now you can work it around
> by having an explicit padding field and setting it to zero?

Yes,the issue isn't blocking for me.
> For a long-term fix, I'm leaning towards adding new umem flag as
> a signal to the kernel to interpret this as a tx_metadata_len. But
> this is gonna break any existing users that set this value. Hopefully
> should not be a lot of them since it is a pretty recent functionality.
>
> I'm also gonna sprinkle some compile time asserts to make sure we can extend
> xdp_umem_reg in the future without hitting the same issue again. I'm a
> bit spoiled by sys_bpf which takes care of enforcing the padding being
> zero.

Sounds good to me, I cannot think of any non-breaking solution.
Thank you for taking care of the issue!

>
> Magnus, any better ideas?


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: xdp/xsk.c: Possible bug in xdp_umem_reg version check
  2024-07-11  3:48         ` Stanislav Fomichev
  2024-07-11  5:23           ` Julian Schindel
@ 2024-07-11  8:11           ` Magnus Karlsson
  1 sibling, 0 replies; 8+ messages in thread
From: Magnus Karlsson @ 2024-07-11  8:11 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Julian Schindel, bpf, Björn Töpel, Magnus Karlsson,
	Maciej Fijalkowski, Stanislav Fomichev, netdev

On Thu, 11 Jul 2024 at 05:48, Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 07/10, Julian Schindel wrote:
> > On 10.07.24 06:45, Stanislav Fomichev wrote:
> > > On 07/09, Julian Schindel wrote:
> > >> On 09.07.24 11:23, Magnus Karlsson wrote:
> > >>> On Sun, 7 Jul 2024 at 17:06, Julian Schindel <mail@arctic-alpaca.de> wrote:
> > >>>> Hi,
> > >>>>
> > >>>> [...]
> > >>> Thank you for reporting this Julian. This seems to be a bug. If I
> > >>> check the value of sizeof(struct xdp_umem_reg_v2), I get 32 bytes too
> > >>> on my system, compiling with gcc 11.4. I am not a compiler guy so do
> > >>> not know what the rules are for padding structs, but I read the
> > >>> following from [0]:
> > >>>
> > >>> "Pad the entire struct to a multiple of 64-bits if the structure
> > >>> contains 64-bit types - the structure size will otherwise differ on
> > >>> 32-bit versus 64-bit. Having a different structure size hurts when
> > >>> passing arrays of structures to the kernel, or if the kernel checks
> > >>> the structure size, which e.g. the drm core does."
> > >>>
> > >>> I compiled for 64-bits and I believe you did too, but we still get
> > >>> this padding.
> > >> Yes, I did also compile for 64-bits. If I understood the resource you
> > >> linked correctly, the compiler automatically adding padding to align to
> > >> 64-bit boundaries is expected for 64-bit platforms:
> > >>
> > >> "[...] 32-bit platforms don’t necessarily align 64-bit values to 64-bit
> > >> boundaries, but 64-bit platforms do. So we always need padding to the
> > >> natural size to get this right."
> > >>> What is sizeof(struct xdp_umem_reg) for you before the
> > >>> patch that added tx_metadata_len?
> > >> I would expect this to be the same as sizeof(struct xdp_umem_reg_v2)
> > >> after the patch. I'm not sure how to check this with different kernel
> > >> versions.
> > >>
> > >> Maybe the following code helps show all the sizes
> > >> of xdp_umem_reg[_v1/_v2] on my system (compiled with "gcc test.c -o
> > >> test" using gcc 14.1.1):
> > >>
> > >> #include <stdio.h>
> > >> #include <sys/types.h>
> > >>
> > >> typedef __uint32_t __u32;
> > >> typedef __uint64_t __u64;
> > >>
> > >> struct xdp_umem_reg_v1  {
> > >>     __u64 addr; /* Start of packet data area */
> > >>     __u64 len; /* Length of packet data area */
> > >>     __u32 chunk_size;
> > >>     __u32 headroom;
> > >> };
> > >>
> > >> struct xdp_umem_reg_v2 {
> > >>     __u64 addr; /* Start of packet data area */
> > >>     __u64 len; /* Length of packet data area */
> > >>     __u32 chunk_size;
> > >>     __u32 headroom;
> > >>     __u32 flags;
> > >> };
> > >>
> > >> struct xdp_umem_reg {
> > >>     __u64 addr; /* Start of packet data area */
> > >>     __u64 len; /* Length of packet data area */
> > >>     __u32 chunk_size;
> > >>     __u32 headroom;
> > >>     __u32 flags;
> > >>     __u32 tx_metadata_len;
> > >> };
> > >>
> > >> int main() {
> > >>     printf("__u32: \t\t\t %lu\n", sizeof(__u32));
> > >>     printf("__u64: \t\t\t %lu\n", sizeof(__u64));
> > >>     printf("xdp_umem_reg_v1: \t %lu\n", sizeof(struct xdp_umem_reg_v1));
> > >>     printf("xdp_umem_reg_v2: \t %lu\n", sizeof(struct xdp_umem_reg_v2));
> > >>     printf("xdp_umem_reg: \t\t %lu\n", sizeof(struct xdp_umem_reg));
> > >> }
> > >>
> > >> Running "./test" produced this output:
> > >>
> > >> __u32:                   4
> > >> __u64:                   8
> > >> xdp_umem_reg_v1:         24
> > >> xdp_umem_reg_v2:         32
> > >> xdp_umem_reg:            32
> > >>> [0]: https://www.kernel.org/doc/html/v5.4/ioctl/botching-up-ioctls.html
> > > Hmm, true, this means our version check won't really work :-/ I don't
> > > see a good way to solve it without breaking the uapi. We can either
> > > add some new padding field to xdp_umem_reg to make it larger than _v2.
> > > Or we can add a new flag to signify the presence of tx_metadata_len
> > > and do the validation based on that.
> > >
> > > Btw, what are you using to setup umem? Looking at libxsk, it does
> > > `memset(&mr, 0, sizeof(mr));` which should clear the padding as well.
> >
> > I'm using "setsockopt" directly with Rust bindings and the C
> > representation of Rust structs [1]. I'm guessing the compiler is not
> > zeroing the padding, which is why I encountered the issue.
> >
> > [1]:
> > https://doc.rust-lang.org/reference/type-layout.html#the-c-representation
>
> Awesome, thanks for confirming! I guess for now you can work it around
> by having an explicit padding field and setting it to zero?
>
> For a long-term fix, I'm leaning towards adding new umem flag as
> a signal to the kernel to interpret this as a tx_metadata_len. But
> this is gonna break any existing users that set this value. Hopefully
> should not be a lot of them since it is a pretty recent functionality.
>
> I'm also gonna sprinkle some compile time asserts to make sure we can extend
> xdp_umem_reg in the future without hitting the same issue again. I'm a
> bit spoiled by sys_bpf which takes care of enforcing the padding being
> zero.
>
> Magnus, any better ideas?

Unfortunately not. This is a pest or cholera kind of choice. I agree
with you that the least bad choice here is to break it for the users
of this new functionality as they should be fewer than all the
existing users that do not use the Tx metadata functionality. Really
appreciate your suggestions to put some compile time asserts in there
to make sure we do not make the same mistake again. Thanks.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-07-11  8:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-07 15:05 xdp/xsk.c: Possible bug in xdp_umem_reg version check Julian Schindel
2024-07-09  9:23 ` Magnus Karlsson
2024-07-09 11:25   ` Julian Schindel
2024-07-10  4:45     ` Stanislav Fomichev
2024-07-10  6:32       ` Julian Schindel
2024-07-11  3:48         ` Stanislav Fomichev
2024-07-11  5:23           ` Julian Schindel
2024-07-11  8:11           ` Magnus Karlsson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).