diff mbox series

[SRU,OEM-5.10,Focal,Bionic,2/2] fget: check that the fd still exists after getting a ref to it

Message ID 20220121142616.163592-3-cascardo@canonical.com
State New
Headers show
Series CVE-2021-4083 | expand

Commit Message

Thadeu Lima de Souza Cascardo Jan. 21, 2022, 2:26 p.m. UTC
From: Linus Torvalds <torvalds@linux-foundation.org>

Jann Horn points out that there is another possible race wrt Unix domain
socket garbage collection, somewhat reminiscent of the one fixed in
commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").

See the extended comment about the garbage collection requirements added
to unix_peek_fds() by that commit for details.

The race comes from how we can locklessly look up a file descriptor just
as it is in the process of being closed, and with the right artificial
timing (Jann added a few strategic 'mdelay(500)' calls to do that), the
Unix domain socket garbage collector could see the reference count
decrement of the close() happen before fget() took its reference to the
file and the file was attached onto a new file descriptor.

This is all (intentionally) correct on the 'struct file *' side, with
RCU lookups and lockless reference counting very much part of the
design.  Getting that reference count out of order isn't a problem per
se.

But the garbage collector can get confused by seeing this situation of
having seen a file not having any remaining external references and then
seeing it being attached to an fd.

In commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK") the
fix was to serialize the file descriptor install with the garbage
collector by taking and releasing the unix_gc_lock.

That's not really an option here, but since this all happens when we are
in the process of looking up a file descriptor, we can instead simply
just re-check that the file hasn't been closed in the meantime, and just
re-do the lookup if we raced with a concurrent close() of the same file
descriptor.

Reported-and-tested-by: Jann Horn <jannh@google.com>
Acked-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(backported from commit 054aa8d439b9185d4f5eb9a90282d1ce74772969)
[cascardo: __fcheck_files was renamed to files_lookup_fd_raw at
 bebf684bf330 ("file: Rename __fcheck_files to files_lookup_fd_raw")]
CVE-2021-4083
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
---
 fs/file.c | 4 ++++
 1 file changed, 4 insertions(+)

Comments

Stefan Bader Jan. 26, 2022, 4:41 p.m. UTC | #1
On 21.01.22 15:26, Thadeu Lima de Souza Cascardo wrote:
> From: Linus Torvalds <torvalds@linux-foundation.org>
> 
> Jann Horn points out that there is another possible race wrt Unix domain
> socket garbage collection, somewhat reminiscent of the one fixed in
> commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
> 
> See the extended comment about the garbage collection requirements added
> to unix_peek_fds() by that commit for details.
> 
> The race comes from how we can locklessly look up a file descriptor just
> as it is in the process of being closed, and with the right artificial
> timing (Jann added a few strategic 'mdelay(500)' calls to do that), the
> Unix domain socket garbage collector could see the reference count
> decrement of the close() happen before fget() took its reference to the
> file and the file was attached onto a new file descriptor.
> 
> This is all (intentionally) correct on the 'struct file *' side, with
> RCU lookups and lockless reference counting very much part of the
> design.  Getting that reference count out of order isn't a problem per
> se.
> 
> But the garbage collector can get confused by seeing this situation of
> having seen a file not having any remaining external references and then
> seeing it being attached to an fd.
> 
> In commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK") the
> fix was to serialize the file descriptor install with the garbage
> collector by taking and releasing the unix_gc_lock.
> 
> That's not really an option here, but since this all happens when we are
> in the process of looking up a file descriptor, we can instead simply
> just re-check that the file hasn't been closed in the meantime, and just
> re-do the lookup if we raced with a concurrent close() of the same file
> descriptor.
> 
> Reported-and-tested-by: Jann Horn <jannh@google.com>
> Acked-by: Miklos Szeredi <mszeredi@redhat.com>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> (backported from commit 054aa8d439b9185d4f5eb9a90282d1ce74772969)
> [cascardo: __fcheck_files was renamed to files_lookup_fd_raw at
>   bebf684bf330 ("file: Rename __fcheck_files to files_lookup_fd_raw")]
> CVE-2021-4083
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
> ---

Applied to focal:linux/master-next. Thanks.

-Stefan

>   fs/file.c | 4 ++++
>   1 file changed, 4 insertions(+)
> 
> diff --git a/fs/file.c b/fs/file.c
> index 3c4b9cf7ef17..5913a5db9c2a 100644
> --- a/fs/file.c
> +++ b/fs/file.c
> @@ -698,6 +698,10 @@ static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
>   			file = NULL;
>   		else if (!get_file_rcu_many(file, refs))
>   			goto loop;
> +		else if (__fcheck_files(files, fd) != file) {
> +			fput_many(file, refs);
> +			goto loop;
> +		}
>   	}
>   	rcu_read_unlock();
>
Stefan Bader Jan. 27, 2022, 1:52 p.m. UTC | #2
On 21.01.22 15:26, Thadeu Lima de Souza Cascardo wrote:
> From: Linus Torvalds <torvalds@linux-foundation.org>
> 
> Jann Horn points out that there is another possible race wrt Unix domain
> socket garbage collection, somewhat reminiscent of the one fixed in
> commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
> 
> See the extended comment about the garbage collection requirements added
> to unix_peek_fds() by that commit for details.
> 
> The race comes from how we can locklessly look up a file descriptor just
> as it is in the process of being closed, and with the right artificial
> timing (Jann added a few strategic 'mdelay(500)' calls to do that), the
> Unix domain socket garbage collector could see the reference count
> decrement of the close() happen before fget() took its reference to the
> file and the file was attached onto a new file descriptor.
> 
> This is all (intentionally) correct on the 'struct file *' side, with
> RCU lookups and lockless reference counting very much part of the
> design.  Getting that reference count out of order isn't a problem per
> se.
> 
> But the garbage collector can get confused by seeing this situation of
> having seen a file not having any remaining external references and then
> seeing it being attached to an fd.
> 
> In commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK") the
> fix was to serialize the file descriptor install with the garbage
> collector by taking and releasing the unix_gc_lock.
> 
> That's not really an option here, but since this all happens when we are
> in the process of looking up a file descriptor, we can instead simply
> just re-check that the file hasn't been closed in the meantime, and just
> re-do the lookup if we raced with a concurrent close() of the same file
> descriptor.
> 
> Reported-and-tested-by: Jann Horn <jannh@google.com>
> Acked-by: Miklos Szeredi <mszeredi@redhat.com>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> (backported from commit 054aa8d439b9185d4f5eb9a90282d1ce74772969)
> [cascardo: __fcheck_files was renamed to files_lookup_fd_raw at
>   bebf684bf330 ("file: Rename __fcheck_files to files_lookup_fd_raw")]
> CVE-2021-4083
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
> ---

Applied to bionic:linux/master-next (2/2). Thanks.

-Stefan

>   fs/file.c | 4 ++++
>   1 file changed, 4 insertions(+)
> 
> diff --git a/fs/file.c b/fs/file.c
> index 3c4b9cf7ef17..5913a5db9c2a 100644
> --- a/fs/file.c
> +++ b/fs/file.c
> @@ -698,6 +698,10 @@ static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
>   			file = NULL;
>   		else if (!get_file_rcu_many(file, refs))
>   			goto loop;
> +		else if (__fcheck_files(files, fd) != file) {
> +			fput_many(file, refs);
> +			goto loop;
> +		}
>   	}
>   	rcu_read_unlock();
>
diff mbox series

Patch

diff --git a/fs/file.c b/fs/file.c
index 3c4b9cf7ef17..5913a5db9c2a 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -698,6 +698,10 @@  static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
 			file = NULL;
 		else if (!get_file_rcu_many(file, refs))
 			goto loop;
+		else if (__fcheck_files(files, fd) != file) {
+			fput_many(file, refs);
+			goto loop;
+		}
 	}
 	rcu_read_unlock();