| From 0bdf399342c5acbd817c9098b6c7ed21f1974312 Mon Sep 17 00:00:00 2001 |
| From: Jordan Rife <jrife@google.com> |
| Date: Mon, 21 Aug 2023 16:45:23 -0500 |
| Subject: net: Avoid address overwrite in kernel_connect |
| |
| From: Jordan Rife <jrife@google.com> |
| |
| commit 0bdf399342c5acbd817c9098b6c7ed21f1974312 upstream. |
| |
| BPF programs that run on connect can rewrite the connect address. For |
| the connect system call this isn't a problem, because a copy of the address |
| is made when it is moved into kernel space. However, kernel_connect |
| simply passes through the address it is given, so the caller may observe |
| its address value unexpectedly change. |
| |
| A practical example where this is problematic is where NFS is combined |
| with a system such as Cilium which implements BPF-based load balancing. |
| A common pattern in software-defined storage systems is to have an NFS |
| mount that connects to a persistent virtual IP which in turn maps to an |
| ephemeral server IP. This is usually done to achieve high availability: |
| if your server goes down you can quickly spin up a replacement and remap |
| the virtual IP to that endpoint. With BPF-based load balancing, mounts |
| will forget the virtual IP address when the address rewrite occurs |
| because a pointer to the only copy of that address is passed down the |
| stack. Server failover then breaks, because clients have forgotten the |
| virtual IP address. Reconnects fail and mounts remain broken. This patch |
| was tested by setting up a scenario like this and ensuring that NFS |
| reconnects worked after applying the patch. |
| |
| Signed-off-by: Jordan Rife <jrife@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/socket.c | 6 +++++- |
| 1 file changed, 5 insertions(+), 1 deletion(-) |
| |
| --- a/net/socket.c |
| +++ b/net/socket.c |
| @@ -3637,7 +3637,11 @@ EXPORT_SYMBOL(kernel_accept); |
| int kernel_connect(struct socket *sock, struct sockaddr *addr, int addrlen, |
| int flags) |
| { |
| - return sock->ops->connect(sock, addr, addrlen, flags); |
| + struct sockaddr_storage address; |
| + |
| + memcpy(&address, addr, addrlen); |
| + |
| + return sock->ops->connect(sock, (struct sockaddr *)&address, addrlen, flags); |
| } |
| EXPORT_SYMBOL(kernel_connect); |
| |