This series fixes some issues around socket creation for AF_XDP.
Patch 1 fixes a potential NULL pointer dereference in
xsk_socket__create_shared.
Patch 2 ensures that the umem passed to xsk_socket__create(_shared)
remains unchanged in event of failure.
Patch 3 makes it possible for xsk_socket__create(_shared) to
succeed even if the rx and tx XDP rings have already been set up, by
ignoring the return value of the XDP_RX_RING/XDP_TX_RING setsockopt.
This removes a limitation which existed whereby a user could not retry
socket creation after a previous failed attempt.
It was chosen to solve the problem by ignoring the return values in
libbpf instead of modifying the setsockopt handling code in the kernel
in order to make it possible for the solution to be available across
all kernels, provided a new enough libbpf is available.
This series applies on commit 87d77e59d1ebc31850697341ab15ca013004b81b
Ciara Loftus (3):
libbpf: ensure umem pointer is non-NULL before dereferencing
libbpf: restore umem state after socket create failure
libbpf: ignore return values of setsockopt for XDP rings.
tools/lib/bpf/xsk.c | 66 +++++++++++++++++++++++++--------------------
1 file changed, 37 insertions(+), 29 deletions(-)
--
2.17.1
Calls to xsk_socket__create dereference the umem to access the
fill_save and comp_save pointers. Make sure the umem is non-NULL
before doing this.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and devices")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 3 +++
1 file changed, 3 insertions(+)
From: Magnus Karlsson <hidden> Date: 2021-03-26 09:15:49
On Wed, Mar 24, 2021 at 3:46 PM Ciara Loftus [off-list ref] wrote:
Calls to xsk_socket__create dereference the umem to access the
fill_save and comp_save pointers. Make sure the umem is non-NULL
before doing this.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and devices")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 3 +++
1 file changed, 3 insertions(+)
Thank you for the fix!
Acked-by: Magnus Karlsson <magnus.karlsson@intel.com>
If the call to socket_create fails, the user may want to retry the
socket creation using the same umem. Ensure that the umem is in the
same state on exit if the call failed by restoring the _save pointers
and not unmapping the set of umem rings if those pointers are non NULL.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and devices")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
@@ -854,6 +856,9 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,structxsk_socket*xsk;structxsk_ctx*ctx;interr,ifindex;+structxsk_ring_prod*fsave=umem->fill_save;+structxsk_ring_cons*csave=umem->comp_save;+boolunmap=!fsave;if(!umem||!xsk_ptr||!(rx||tx))return-EFAULT;
@@ -1005,7 +1010,9 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,munmap(rx_map,off.rx.desc+xsk->config.rx_size*sizeof(structxdp_desc));out_put_ctx:-xsk_put_ctx(ctx);+umem->fill_save=fsave;+umem->comp_save=csave;+xsk_put_ctx(ctx,unmap);out_socket:if(--umem->refcount)close(xsk->fd);
@@ -1071,7 +1078,7 @@ void xsk_socket__delete(struct xsk_socket *xsk)}}-xsk_put_ctx(ctx);+xsk_put_ctx(ctx,true);umem->refcount--;/* Do not close an fd that also has an associated umem connected
From: Magnus Karlsson <hidden> Date: 2021-03-26 09:07:19
On Wed, Mar 24, 2021 at 3:46 PM Ciara Loftus [off-list ref] wrote:
quoted hunk
If the call to socket_create fails, the user may want to retry the
socket creation using the same umem. Ensure that the umem is in the
same state on exit if the call failed by restoring the _save pointers
and not unmapping the set of umem rings if those pointers are non NULL.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and devices")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
By not unmapping these rings we actually leave more state after a
failed socket creation. So how about skipping this logic (and
everything below) and always unmap the rings at failure as before, but
we move the fill_save = NULL and comp_save = NULL from xsk_create_ctx
to the end of xsk_socket__create_shared just before the "return 0"
where we know that the whole operation has succeeded. This way the
mappings would be redone during the next xsk_socket__create and if
someone decides not to retry (for some reason) we do not leave two
mappings behind. Would simplify things. What do you think?
@@ -1071,7 +1078,7 @@ void xsk_socket__delete(struct xsk_socket *xsk) } }- xsk_put_ctx(ctx);+ xsk_put_ctx(ctx, true); umem->refcount--; /* Do not close an fd that also has an associated umem connected--
On Wed, Mar 24, 2021 at 3:46 PM Ciara Loftus [off-list ref]
wrote:
quoted
If the call to socket_create fails, the user may want to retry the
socket creation using the same umem. Ensure that the umem is in the
same state on exit if the call failed by restoring the _save pointers
and not unmapping the set of umem rings if those pointers are non NULL.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and
By not unmapping these rings we actually leave more state after a
failed socket creation. So how about skipping this logic (and
In the case of the _save rings, the maps existed before the call to
xsk_socket__create. They were created during xsk_umem__create.
So we should preserve these maps in event of failure.
I was using the wrong condition to trigger the unmap in v1 however.
We should unmap 'fill' only if
umem->fill_save != fill
I will update this in a v2.
everything below) and always unmap the rings at failure as before, but
we move the fill_save = NULL and comp_save = NULL from xsk_create_ctx
to the end of xsk_socket__create_shared just before the "return 0"
where we know that the whole operation has succeeded. This way the
I think moving these still makes sense and will add this in the next rev.
Thanks for the feedback and suggestions!
Ciara
mappings would be redone during the next xsk_socket__create and if
someone decides not to retry (for some reason) we do not leave two
mappings behind. Would simplify things. What do you think?
quoted
list_del(&ctx->list);
@@ -854,6 +856,9 @@ int xsk_socket__create_shared(struct xsk_socket
@@ -1071,7 +1078,7 @@ void xsk_socket__delete(struct xsk_socket *xsk) } }- xsk_put_ctx(ctx);+ xsk_put_ctx(ctx, true); umem->refcount--; /* Do not close an fd that also has an associated umem connected--
From: Magnus Karlsson <hidden> Date: 2021-03-26 15:21:05
On Fri, Mar 26, 2021 at 3:56 PM Loftus, Ciara [off-list ref] wrote:
quoted
On Wed, Mar 24, 2021 at 3:46 PM Ciara Loftus [off-list ref]
wrote:
quoted
If the call to socket_create fails, the user may want to retry the
socket creation using the same umem. Ensure that the umem is in the
same state on exit if the call failed by restoring the _save pointers
and not unmapping the set of umem rings if those pointers are non NULL.
Fixes: 2f6324a3937f ("libbpf: Support shared umems between queues and
By not unmapping these rings we actually leave more state after a
failed socket creation. So how about skipping this logic (and
In the case of the _save rings, the maps existed before the call to
xsk_socket__create. They were created during xsk_umem__create.
So we should preserve these maps in event of failure.
I was using the wrong condition to trigger the unmap in v1 however.
We should unmap 'fill' only if
umem->fill_save != fill
I will update this in a v2.
Ahh, you are correct. There are two ways these rings can get allocated
so that has to be taken care of. Please ignore my comment.
quoted
everything below) and always unmap the rings at failure as before, but
we move the fill_save = NULL and comp_save = NULL from xsk_create_ctx
to the end of xsk_socket__create_shared just before the "return 0"
where we know that the whole operation has succeeded. This way the
I think moving these still makes sense and will add this in the next rev.
Thanks for the feedback and suggestions!
Ciara
quoted
mappings would be redone during the next xsk_socket__create and if
someone decides not to retry (for some reason) we do not leave two
mappings behind. Would simplify things. What do you think?
quoted
quoted
list_del(&ctx->list);
@@ -854,6 +856,9 @@ int xsk_socket__create_shared(struct xsk_socket
@@ -1071,7 +1078,7 @@ void xsk_socket__delete(struct xsk_socket *xsk) } }- xsk_put_ctx(ctx);+ xsk_put_ctx(ctx, true); umem->refcount--; /* Do not close an fd that also has an associated umem connected--
During xsk_socket__create the XDP_RX_RING and XDP_TX_RING setsockopts
are called to create the rx and tx rings for the AF_XDP socket. If the ring
has already been set up, the setsockopt will return an error. However,
in the event of a failure during xsk_socket__create(_shared) after the
rings have been set up, the user may wish to retry the socket creation
using these pre-existing rings. In this case we can ignore the error
returned by the setsockopts. If there is a true error, the subsequent
call to mmap() will catch it.
Fixes: 1cad07884239 ("libbpf: add support for using AF_XDP sockets")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
@@ -904,24 +904,22 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,}xsk->ctx=ctx;-if(rx){-err=setsockopt(xsk->fd,SOL_XDP,XDP_RX_RING,-&xsk->config.rx_size,-sizeof(xsk->config.rx_size));-if(err){-err=-errno;-gotoout_put_ctx;-}-}-if(tx){-err=setsockopt(xsk->fd,SOL_XDP,XDP_TX_RING,-&xsk->config.tx_size,-sizeof(xsk->config.tx_size));-if(err){-err=-errno;-gotoout_put_ctx;-}-}+/* The return values of these setsockopt calls are intentionally not checked.+*Iftheringhasalreadybeensetupsetsockoptwillreturnanerror.However,+*thisscenarioisacceptableastheusermayberetryingthesocketcreation+*withringswhichweresetupinapreviousbutultimatelyunsuccessfulcall+*toxsk_socket__create(_shared).Thecalllatertommap()willfailifthere+*isarealissueandwehandlethatreturnvalueappropriatelythere.+*/+if(rx)+setsockopt(xsk->fd,SOL_XDP,XDP_RX_RING,+&xsk->config.rx_size,+sizeof(xsk->config.rx_size));++if(tx)+setsockopt(xsk->fd,SOL_XDP,XDP_TX_RING,+&xsk->config.tx_size,+sizeof(xsk->config.tx_size));err=xsk_get_mmap_offsets(xsk->fd,&off);if(err){
From: Magnus Karlsson <hidden> Date: 2021-03-26 09:15:17
On Wed, Mar 24, 2021 at 3:46 PM Ciara Loftus [off-list ref] wrote:
quoted hunk
During xsk_socket__create the XDP_RX_RING and XDP_TX_RING setsockopts
are called to create the rx and tx rings for the AF_XDP socket. If the ring
has already been set up, the setsockopt will return an error. However,
in the event of a failure during xsk_socket__create(_shared) after the
rings have been set up, the user may wish to retry the socket creation
using these pre-existing rings. In this case we can ignore the error
returned by the setsockopts. If there is a true error, the subsequent
call to mmap() will catch it.
Fixes: 1cad07884239 ("libbpf: add support for using AF_XDP sockets")
Signed-off-by: Ciara Loftus <redacted>
---
tools/lib/bpf/xsk.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
@@ -904,24 +904,22 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,}xsk->ctx=ctx;-if(rx){-err=setsockopt(xsk->fd,SOL_XDP,XDP_RX_RING,-&xsk->config.rx_size,-sizeof(xsk->config.rx_size));-if(err){-err=-errno;-gotoout_put_ctx;-}-}-if(tx){-err=setsockopt(xsk->fd,SOL_XDP,XDP_TX_RING,-&xsk->config.tx_size,-sizeof(xsk->config.tx_size));-if(err){-err=-errno;-gotoout_put_ctx;-}-}+/* The return values of these setsockopt calls are intentionally not checked.+*Iftheringhasalreadybeensetupsetsockoptwillreturnanerror.However,+*thisscenarioisacceptableastheusermayberetryingthesocketcreation+*withringswhichweresetupinapreviousbutultimatelyunsuccessfulcall+*toxsk_socket__create(_shared).Thecalllatertommap()willfailifthere+*isarealissueandwehandlethatreturnvalueappropriatelythere.+*/+if(rx)+setsockopt(xsk->fd,SOL_XDP,XDP_RX_RING,+&xsk->config.rx_size,+sizeof(xsk->config.rx_size));++if(tx)+setsockopt(xsk->fd,SOL_XDP,XDP_TX_RING,+&xsk->config.tx_size,+sizeof(xsk->config.tx_size));
Thanks Ciara!
This is a pragmatic solution, but I do not see any better way around
it since these operations are irreversible. And it works without any
fix to the kernel which is good and you have a comment explaining
things clearly. With that said, it would be nice as a follow up to
bpf-next to actually return a unique error value (among the ones that
this function can return) when the rings have already been mapped.
This way the user can react to this in a more informed way in the
future.
Acked-by: Magnus Karlsson <magnus.karlsson@intel.com>
err = xsk_get_mmap_offsets(xsk->fd, &off);
if (err) {
--
2.17.1