Skip to content

Commit 4bfe6cc

Browse files
jaytlangKAGA-KOKO
authored andcommitted
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: ea5f1cd ("x86/ioperm: Remove bitmap if all permissions dropped") Signed-off-by: Jay Lang <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: stable#@vger.kernel.org Link: https://lkml.kernel.org/r/[email protected]
1 parent 8874347 commit 4bfe6cc

File tree

3 files changed

+15
-15
lines changed

3 files changed

+15
-15
lines changed

arch/x86/include/asm/io_bitmap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct task_struct;
1717

1818
#ifdef CONFIG_X86_IOPL_IOPERM
1919
void io_bitmap_share(struct task_struct *tsk);
20-
void io_bitmap_exit(void);
20+
void io_bitmap_exit(struct task_struct *tsk);
2121

2222
void native_tss_update_io_bitmap(void);
2323

@@ -29,7 +29,7 @@ void native_tss_update_io_bitmap(void);
2929

3030
#else
3131
static inline void io_bitmap_share(struct task_struct *tsk) { }
32-
static inline void io_bitmap_exit(void) { }
32+
static inline void io_bitmap_exit(struct task_struct *tsk) { }
3333
static inline void tss_update_io_bitmap(void) { }
3434
#endif
3535

arch/x86/kernel/ioport.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,28 @@ void io_bitmap_share(struct task_struct *tsk)
3333
set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
3434
}
3535

36-
static void task_update_io_bitmap(void)
36+
static void task_update_io_bitmap(struct task_struct *tsk)
3737
{
38-
struct thread_struct *t = &current->thread;
38+
struct thread_struct *t = &tsk->thread;
3939

4040
if (t->iopl_emul == 3 || t->io_bitmap) {
4141
/* TSS update is handled on exit to user space */
42-
set_thread_flag(TIF_IO_BITMAP);
42+
set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
4343
} else {
44-
clear_thread_flag(TIF_IO_BITMAP);
44+
clear_tsk_thread_flag(tsk, TIF_IO_BITMAP);
4545
/* Invalidate TSS */
4646
preempt_disable();
4747
tss_update_io_bitmap();
4848
preempt_enable();
4949
}
5050
}
5151

52-
void io_bitmap_exit(void)
52+
void io_bitmap_exit(struct task_struct *tsk)
5353
{
54-
struct io_bitmap *iobm = current->thread.io_bitmap;
54+
struct io_bitmap *iobm = tsk->thread.io_bitmap;
5555

56-
current->thread.io_bitmap = NULL;
57-
task_update_io_bitmap();
56+
tsk->thread.io_bitmap = NULL;
57+
task_update_io_bitmap(tsk);
5858
if (iobm && refcount_dec_and_test(&iobm->refcnt))
5959
kfree(iobm);
6060
}
@@ -102,7 +102,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
102102
if (!iobm)
103103
return -ENOMEM;
104104
refcount_set(&iobm->refcnt, 1);
105-
io_bitmap_exit();
105+
io_bitmap_exit(current);
106106
}
107107

108108
/*
@@ -134,7 +134,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
134134
}
135135
/* All permissions dropped? */
136136
if (max_long == UINT_MAX) {
137-
io_bitmap_exit();
137+
io_bitmap_exit(current);
138138
return 0;
139139
}
140140

@@ -192,7 +192,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level)
192192
}
193193

194194
t->iopl_emul = level;
195-
task_update_io_bitmap();
195+
task_update_io_bitmap(current);
196196

197197
return 0;
198198
}

arch/x86/kernel/process.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
9696
}
9797

9898
/*
99-
* Free current thread data structures etc..
99+
* Free thread data structures etc..
100100
*/
101101
void exit_thread(struct task_struct *tsk)
102102
{
103103
struct thread_struct *t = &tsk->thread;
104104
struct fpu *fpu = &t->fpu;
105105

106106
if (test_thread_flag(TIF_IO_BITMAP))
107-
io_bitmap_exit();
107+
io_bitmap_exit(tsk);
108108

109109
free_vm86(t);
110110

0 commit comments

Comments
 (0)