linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] x86/ioperm: fix a memory leak bug
@ 2020-05-24 16:27 Jay Lang
  2020-05-28 19:30 ` Thomas Gleixner
  2020-05-28 19:38 ` [tip: x86/urgent] x86/ioperm: Prevent a memory leak when fork fails tip-bot2 for Jay Lang
  0 siblings, 2 replies; 3+ messages in thread
From: Jay Lang @ 2020-05-24 16:27 UTC (permalink / raw)
  Cc: jaytlang, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86,
	H. Peter Anvin, Christian Brauner, Andy Lutomirski,
	Juergen Gross, Jan Beulich, Benjamin Thiel, Matthew Garrett,
	Peter Zijlstra (Intel),
	Brian Gerst, Marcelo Tosatti, yu kuai, linux-kernel

In the copy_process() routine called by _do_fork(), failure to allocate
a PID (or further along in the function) will trigger an invocation to
exit_thread(). This is done to clean up from an earlier call to
copy_thread_tls(). Naturally, the child task is passed into exit_thread(),
however during the process, io_bitmap_exit() nullifies the parent's
io_bitmap rather than the child's.

As copy_thread_tls() has been called ahead of the failure, the reference
count on the calling thread's io_bitmap is incremented as we would expect.
However, io_bitmap_exit() doesn't accept any arguments, and thus assumes
it should trash the current thread's io_bitmap reference rather than the
child's. This is pretty sneaky in practice, because in all instances but
this one, exit_thread() is called with respect to the current task and
everything works out.

A determined attacker can issue an appropriate ioctl (i.e. KDENABIO) to
get a bitmap allocated, and force a clone3() syscall to fail by passing
in a zeroed clone_args structure. The kernel handles the erroneous struct
and the buggy code path is followed, and even though the parent's reference
to the io_bitmap is trashed, the child still holds a reference and thus
the structure will never be freed.

Fix this by tweaking io_bitmap_exit() and its subroutines to accept a
task_struct argument which to operate on. This may not be the most elegant
solution, but it mitigates the trigger described above on an x86_64 kernel.

Signed-off-by: Jay Lang <jaytlang@mit.edu>
---
 arch/x86/include/asm/io_bitmap.h |  4 ++--
 arch/x86/kernel/ioport.c         | 22 +++++++++++-----------
 arch/x86/kernel/process.c        |  4 ++--
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/x86/include/asm/io_bitmap.h b/arch/x86/include/asm/io_bitmap.h
index 07344d82e88e..ac1a99ffbd8d 100644
--- a/arch/x86/include/asm/io_bitmap.h
+++ b/arch/x86/include/asm/io_bitmap.h
@@ -17,7 +17,7 @@ struct task_struct;
 
 #ifdef CONFIG_X86_IOPL_IOPERM
 void io_bitmap_share(struct task_struct *tsk);
-void io_bitmap_exit(void);
+void io_bitmap_exit(struct task_struct *tsk);
 
 void native_tss_update_io_bitmap(void);
 
@@ -29,7 +29,7 @@ void native_tss_update_io_bitmap(void);
 
 #else
 static inline void io_bitmap_share(struct task_struct *tsk) { }
-static inline void io_bitmap_exit(void) { }
+static inline void io_bitmap_exit(struct task_struct *tsk) { }
 static inline void tss_update_io_bitmap(void) { }
 #endif
 
diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c
index a53e7b4a7419..e2fab3ceb09f 100644
--- a/arch/x86/kernel/ioport.c
+++ b/arch/x86/kernel/ioport.c
@@ -33,15 +33,15 @@ void io_bitmap_share(struct task_struct *tsk)
 	set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 }
 
-static void task_update_io_bitmap(void)
+static void task_update_io_bitmap(struct task_struct *tsk)
 {
-	struct thread_struct *t = &current->thread;
+	struct thread_struct *t = &tsk->thread;
 
 	if (t->iopl_emul == 3 || t->io_bitmap) {
 		/* TSS update is handled on exit to user space */
-		set_thread_flag(TIF_IO_BITMAP);
+		set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 	} else {
-		clear_thread_flag(TIF_IO_BITMAP);
+		clear_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 		/* Invalidate TSS */
 		preempt_disable();
 		tss_update_io_bitmap();
@@ -49,12 +49,12 @@ static void task_update_io_bitmap(void)
 	}
 }
 
-void io_bitmap_exit(void)
+void io_bitmap_exit(struct task_struct *tsk)
 {
-	struct io_bitmap *iobm = current->thread.io_bitmap;
+	struct io_bitmap *iobm = tsk->thread.io_bitmap;
 
-	current->thread.io_bitmap = NULL;
-	task_update_io_bitmap();
+	tsk->thread.io_bitmap = NULL;
+	task_update_io_bitmap(tsk);
 	if (iobm && refcount_dec_and_test(&iobm->refcnt))
 		kfree(iobm);
 }
@@ -102,7 +102,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
 		if (!iobm)
 			return -ENOMEM;
 		refcount_set(&iobm->refcnt, 1);
-		io_bitmap_exit();
+		io_bitmap_exit(current);
 	}
 
 	/*
@@ -134,7 +134,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
 	}
 	/* All permissions dropped? */
 	if (max_long == UINT_MAX) {
-		io_bitmap_exit();
+		io_bitmap_exit(current);
 		return 0;
 	}
 
@@ -192,7 +192,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level)
 	}
 
 	t->iopl_emul = level;
-	task_update_io_bitmap();
+	task_update_io_bitmap(current);
 
 	return 0;
 }
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 9da70b279dad..35638f1c5791 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -96,7 +96,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 }
 
 /*
- * Free current thread data structures etc..
+ * Free thread data structures etc..
  */
 void exit_thread(struct task_struct *tsk)
 {
@@ -104,7 +104,7 @@ void exit_thread(struct task_struct *tsk)
 	struct fpu *fpu = &t->fpu;
 
 	if (test_thread_flag(TIF_IO_BITMAP))
-		io_bitmap_exit();
+		io_bitmap_exit(tsk);
 
 	free_vm86(t);
 
-- 
2.25.1


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

* Re: [PATCH] x86/ioperm: fix a memory leak bug
  2020-05-24 16:27 [PATCH] x86/ioperm: fix a memory leak bug Jay Lang
@ 2020-05-28 19:30 ` Thomas Gleixner
  2020-05-28 19:38 ` [tip: x86/urgent] x86/ioperm: Prevent a memory leak when fork fails tip-bot2 for Jay Lang
  1 sibling, 0 replies; 3+ messages in thread
From: Thomas Gleixner @ 2020-05-28 19:30 UTC (permalink / raw)
  To: Jay Lang
  Cc: jaytlang, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin,
	Christian Brauner, Andy Lutomirski, Juergen Gross, Jan Beulich,
	Benjamin Thiel, Matthew Garrett, Peter Zijlstra (Intel),
	Brian Gerst, Marcelo Tosatti, yu kuai, linux-kernel

Jay,

Jay Lang <jaytlang@mit.edu> writes:

> In the copy_process() routine called by _do_fork(), failure to allocate
> a PID (or further along in the function) will trigger an invocation to
> exit_thread(). This is done to clean up from an earlier call to
> copy_thread_tls(). Naturally, the child task is passed into exit_thread(),
> however during the process, io_bitmap_exit() nullifies the parent's
> io_bitmap rather than the child's.
>
> As copy_thread_tls() has been called ahead of the failure, the reference
> count on the calling thread's io_bitmap is incremented as we would expect.
> However, io_bitmap_exit() doesn't accept any arguments, and thus assumes
> it should trash the current thread's io_bitmap reference rather than the
> child's. This is pretty sneaky in practice, because in all instances but
> this one, exit_thread() is called with respect to the current task and
> everything works out.
>
> A determined attacker can issue an appropriate ioctl (i.e. KDENABIO) to
> get a bitmap allocated, and force a clone3() syscall to fail by passing
> in a zeroed clone_args structure. The kernel handles the erroneous struct
> and the buggy code path is followed, and even though the parent's reference
> to the io_bitmap is trashed, the child still holds a reference and thus
> the structure will never be freed.

Nice catch! I'm sure I thought about that at some point and then forgot
again.

> Fix this by tweaking io_bitmap_exit() and its subroutines to accept a
> task_struct argument which to operate on.

> This may not be the most elegant solution, but it mitigates the
> trigger described above on an x86_64 kernel.

It's well done and straight forward and comes with a well done change
log. Appreciated!

Thanks,

        tglx

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

* [tip: x86/urgent] x86/ioperm: Prevent a memory leak when fork fails
  2020-05-24 16:27 [PATCH] x86/ioperm: fix a memory leak bug Jay Lang
  2020-05-28 19:30 ` Thomas Gleixner
@ 2020-05-28 19:38 ` tip-bot2 for Jay Lang
  1 sibling, 0 replies; 3+ messages in thread
From: tip-bot2 for Jay Lang @ 2020-05-28 19:38 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: Jay Lang, Thomas Gleixner, stable#, x86, LKML

The following commit has been merged into the x86/urgent branch of tip:

Commit-ID:     4bfe6cce133cad82cea04490c308795275857782
Gitweb:        https://git.kernel.org/tip/4bfe6cce133cad82cea04490c308795275857782
Author:        Jay Lang <jaytlang@mit.edu>
AuthorDate:    Sun, 24 May 2020 12:27:39 -04:00
Committer:     Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 28 May 2020 21:36:20 +02:00

x86/ioperm: Prevent a memory leak when fork fails

In the copy_process() routine called by _do_fork(), failure to allocate
a PID (or further along in the function) will trigger an invocation to
exit_thread(). This is done to clean up from an earlier call to
copy_thread_tls(). Naturally, the child task is passed into exit_thread(),
however during the process, io_bitmap_exit() nullifies the parent's
io_bitmap rather than the child's.

As copy_thread_tls() has been called ahead of the failure, the reference
count on the calling thread's io_bitmap is incremented as we would expect.
However, io_bitmap_exit() doesn't accept any arguments, and thus assumes
it should trash the current thread's io_bitmap reference rather than the
child's. This is pretty sneaky in practice, because in all instances but
this one, exit_thread() is called with respect to the current task and
everything works out.

A determined attacker can issue an appropriate ioctl (i.e. KDENABIO) to
get a bitmap allocated, and force a clone3() syscall to fail by passing
in a zeroed clone_args structure. The kernel handles the erroneous struct
and the buggy code path is followed, and even though the parent's reference
to the io_bitmap is trashed, the child still holds a reference and thus
the structure will never be freed.

Fix this by tweaking io_bitmap_exit() and its subroutines to accept a
task_struct argument which to operate on.

Fixes: ea5f1cd7ab49 ("x86/ioperm: Remove bitmap if all permissions dropped")
Signed-off-by: Jay Lang <jaytlang@mit.edu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable#@vger.kernel.org
Link: https://lkml.kernel.org/r/20200524162742.253727-1-jaytlang@mit.edu
---
 arch/x86/include/asm/io_bitmap.h |  4 ++--
 arch/x86/kernel/ioport.c         | 22 +++++++++++-----------
 arch/x86/kernel/process.c        |  4 ++--
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/x86/include/asm/io_bitmap.h b/arch/x86/include/asm/io_bitmap.h
index 07344d8..ac1a99f 100644
--- a/arch/x86/include/asm/io_bitmap.h
+++ b/arch/x86/include/asm/io_bitmap.h
@@ -17,7 +17,7 @@ struct task_struct;
 
 #ifdef CONFIG_X86_IOPL_IOPERM
 void io_bitmap_share(struct task_struct *tsk);
-void io_bitmap_exit(void);
+void io_bitmap_exit(struct task_struct *tsk);
 
 void native_tss_update_io_bitmap(void);
 
@@ -29,7 +29,7 @@ void native_tss_update_io_bitmap(void);
 
 #else
 static inline void io_bitmap_share(struct task_struct *tsk) { }
-static inline void io_bitmap_exit(void) { }
+static inline void io_bitmap_exit(struct task_struct *tsk) { }
 static inline void tss_update_io_bitmap(void) { }
 #endif
 
diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c
index a53e7b4..e2fab3c 100644
--- a/arch/x86/kernel/ioport.c
+++ b/arch/x86/kernel/ioport.c
@@ -33,15 +33,15 @@ void io_bitmap_share(struct task_struct *tsk)
 	set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 }
 
-static void task_update_io_bitmap(void)
+static void task_update_io_bitmap(struct task_struct *tsk)
 {
-	struct thread_struct *t = &current->thread;
+	struct thread_struct *t = &tsk->thread;
 
 	if (t->iopl_emul == 3 || t->io_bitmap) {
 		/* TSS update is handled on exit to user space */
-		set_thread_flag(TIF_IO_BITMAP);
+		set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 	} else {
-		clear_thread_flag(TIF_IO_BITMAP);
+		clear_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 		/* Invalidate TSS */
 		preempt_disable();
 		tss_update_io_bitmap();
@@ -49,12 +49,12 @@ static void task_update_io_bitmap(void)
 	}
 }
 
-void io_bitmap_exit(void)
+void io_bitmap_exit(struct task_struct *tsk)
 {
-	struct io_bitmap *iobm = current->thread.io_bitmap;
+	struct io_bitmap *iobm = tsk->thread.io_bitmap;
 
-	current->thread.io_bitmap = NULL;
-	task_update_io_bitmap();
+	tsk->thread.io_bitmap = NULL;
+	task_update_io_bitmap(tsk);
 	if (iobm && refcount_dec_and_test(&iobm->refcnt))
 		kfree(iobm);
 }
@@ -102,7 +102,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
 		if (!iobm)
 			return -ENOMEM;
 		refcount_set(&iobm->refcnt, 1);
-		io_bitmap_exit();
+		io_bitmap_exit(current);
 	}
 
 	/*
@@ -134,7 +134,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
 	}
 	/* All permissions dropped? */
 	if (max_long == UINT_MAX) {
-		io_bitmap_exit();
+		io_bitmap_exit(current);
 		return 0;
 	}
 
@@ -192,7 +192,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level)
 	}
 
 	t->iopl_emul = level;
-	task_update_io_bitmap();
+	task_update_io_bitmap(current);
 
 	return 0;
 }
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 9da70b2..35638f1 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -96,7 +96,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 }
 
 /*
- * Free current thread data structures etc..
+ * Free thread data structures etc..
  */
 void exit_thread(struct task_struct *tsk)
 {
@@ -104,7 +104,7 @@ void exit_thread(struct task_struct *tsk)
 	struct fpu *fpu = &t->fpu;
 
 	if (test_thread_flag(TIF_IO_BITMAP))
-		io_bitmap_exit();
+		io_bitmap_exit(tsk);
 
 	free_vm86(t);
 

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

end of thread, other threads:[~2020-05-28 19:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-24 16:27 [PATCH] x86/ioperm: fix a memory leak bug Jay Lang
2020-05-28 19:30 ` Thomas Gleixner
2020-05-28 19:38 ` [tip: x86/urgent] x86/ioperm: Prevent a memory leak when fork fails tip-bot2 for Jay Lang

This is a public inbox, see mirroring instructions
on how to clone and mirror all data and code used for this inbox