Skip to content

Commit 22fe5b0

Browse files
committed
x86/ioperm: Move TSS bitmap update to exit to user work
There is no point to update the TSS bitmap for tasks which use I/O bitmaps on every context switch. It's enough to update it right before exiting to user space. That reduces the context switch bitmap handling to invalidating the io bitmap base offset in the TSS when the outgoing task has TIF_IO_BITMAP set. The invaldiation is done on purpose when a task with an IO bitmap switches out to prevent any possible leakage of an activated IO bitmap. It also removes the requirement to update the tasks bitmap atomically in ioperm(). Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 060aa16 commit 22fe5b0

File tree

5 files changed

+54
-45
lines changed

5 files changed

+54
-45
lines changed

arch/x86/entry/common.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <asm/cpufeature.h>
3434
#include <asm/fpu/api.h>
3535
#include <asm/nospec-branch.h>
36+
#include <asm/io_bitmap.h>
3637

3738
#define CREATE_TRACE_POINTS
3839
#include <trace/events/syscalls.h>
@@ -196,6 +197,9 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
196197
/* Reload ti->flags; we may have rescheduled above. */
197198
cached_flags = READ_ONCE(ti->flags);
198199

200+
if (unlikely(cached_flags & _TIF_IO_BITMAP))
201+
tss_update_io_bitmap();
202+
199203
fpregs_assert_state_consistent();
200204
if (unlikely(cached_flags & _TIF_NEED_FPU_LOAD))
201205
switch_fpu_return();

arch/x86/include/asm/io_bitmap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ struct io_bitmap {
1111
unsigned long bitmap[IO_BITMAP_LONGS];
1212
};
1313

14+
void tss_update_io_bitmap(void);
15+
1416
#endif

arch/x86/include/asm/thread_info.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ struct thread_info {
143143
_TIF_NOHZ)
144144

145145
/* flags to check in __switch_to() */
146-
#define _TIF_WORK_CTXSW_BASE \
147-
(_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP| \
146+
#define _TIF_WORK_CTXSW_BASE \
147+
(_TIF_NOCPUID | _TIF_NOTSC | _TIF_BLOCKSTEP | \
148148
_TIF_SSBD | _TIF_SPEC_FORCE_UPDATE)
149149

150150
/*
@@ -156,8 +156,9 @@ struct thread_info {
156156
# define _TIF_WORK_CTXSW (_TIF_WORK_CTXSW_BASE)
157157
#endif
158158

159-
#define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
160-
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
159+
#define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW| _TIF_USER_RETURN_NOTIFY | \
160+
_TIF_IO_BITMAP)
161+
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
161162

162163
#define STACK_WARN (THREAD_SIZE/8)
163164

arch/x86/kernel/ioport.c

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ static atomic64_t io_bitmap_sequence;
2121
*/
2222
long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
2323
{
24-
unsigned int i, max_long, bytes, bytes_updated;
2524
struct thread_struct *t = &current->thread;
26-
struct tss_struct *tss;
25+
unsigned int i, max_long;
2726
struct io_bitmap *iobm;
2827

2928
if ((from + num <= from) || (from + num > IO_BITMAP_BITS))
@@ -50,10 +49,9 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
5049
}
5150

5251
/*
53-
* Update the bitmap and the TSS copy with preemption disabled to
54-
* prevent a race against context switch.
52+
* Update the tasks bitmap. The update of the TSS bitmap happens on
53+
* exit to user mode. So this needs no protection.
5554
*/
56-
preempt_disable();
5755
if (turn_on)
5856
bitmap_clear(iobm->bitmap, from, num);
5957
else
@@ -69,11 +67,8 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
6967
max_long = i;
7068
}
7169

72-
bytes = (max_long + 1) * sizeof(unsigned long);
73-
bytes_updated = max(bytes, t->io_bitmap->max);
70+
iobm->max = (max_long + 1) * sizeof(unsigned long);
7471

75-
/* Update the thread data */
76-
iobm->max = bytes;
7772
/* Update the sequence number to force an update in switch_to() */
7873
iobm->sequence = atomic64_add_return(1, &io_bitmap_sequence);
7974

@@ -85,18 +80,6 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
8580
t->io_bitmap = iobm;
8681
set_thread_flag(TIF_IO_BITMAP);
8782

88-
/* Update the TSS */
89-
tss = this_cpu_ptr(&cpu_tss_rw);
90-
memcpy(tss->io_bitmap.bitmap, iobm->bitmap, bytes_updated);
91-
/* Store the new end of the zero bits */
92-
tss->io_bitmap.prev_max = bytes;
93-
/* Make the bitmap base in the TSS valid */
94-
tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET_VALID;
95-
/* Make sure the TSS limit covers the I/O bitmap. */
96-
refresh_tss_limit();
97-
98-
preempt_enable();
99-
10083
return 0;
10184
}
10285

arch/x86/kernel/process.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,34 @@ void arch_setup_new_exec(void)
360360
}
361361
}
362362

363-
static void switch_to_update_io_bitmap(struct tss_struct *tss,
364-
struct io_bitmap *iobm)
363+
static inline void tss_invalidate_io_bitmap(struct tss_struct *tss)
364+
{
365+
/*
366+
* Invalidate the I/O bitmap by moving io_bitmap_base outside the
367+
* TSS limit so any subsequent I/O access from user space will
368+
* trigger a #GP.
369+
*
370+
* This is correct even when VMEXIT rewrites the TSS limit
371+
* to 0x67 as the only requirement is that the base points
372+
* outside the limit.
373+
*/
374+
tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET_INVALID;
375+
}
376+
377+
static inline void switch_to_bitmap(unsigned long tifp)
378+
{
379+
/*
380+
* Invalidate I/O bitmap if the previous task used it. This prevents
381+
* any possible leakage of an active I/O bitmap.
382+
*
383+
* If the next task has an I/O bitmap it will handle it on exit to
384+
* user mode.
385+
*/
386+
if (tifp & _TIF_IO_BITMAP)
387+
tss_invalidate_io_bitmap(this_cpu_ptr(&cpu_tss_rw));
388+
}
389+
390+
static void tss_copy_io_bitmap(struct tss_struct *tss, struct io_bitmap *iobm)
365391
{
366392
/*
367393
* Copy at least the byte range of the incoming tasks bitmap which
@@ -382,21 +408,23 @@ static void switch_to_update_io_bitmap(struct tss_struct *tss,
382408
tss->io_bitmap.prev_sequence = iobm->sequence;
383409
}
384410

385-
static inline void switch_to_bitmap(struct thread_struct *next,
386-
unsigned long tifp, unsigned long tifn)
411+
/**
412+
* tss_update_io_bitmap - Update I/O bitmap before exiting to usermode
413+
*/
414+
void tss_update_io_bitmap(void)
387415
{
388416
struct tss_struct *tss = this_cpu_ptr(&cpu_tss_rw);
389417

390-
if (tifn & _TIF_IO_BITMAP) {
391-
struct io_bitmap *iobm = next->io_bitmap;
418+
if (test_thread_flag(TIF_IO_BITMAP)) {
419+
struct io_bitmap *iobm = current->thread.io_bitmap;
392420

393421
/*
394422
* Only copy bitmap data when the sequence number
395423
* differs. The update time is accounted to the incoming
396424
* task.
397425
*/
398426
if (tss->io_bitmap.prev_sequence != iobm->sequence)
399-
switch_to_update_io_bitmap(tss, iobm);
427+
tss_copy_io_bitmap(tss, iobm);
400428

401429
/* Enable the bitmap */
402430
tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET_VALID;
@@ -409,18 +437,8 @@ static inline void switch_to_bitmap(struct thread_struct *next,
409437
* limit.
410438
*/
411439
refresh_tss_limit();
412-
} else if (tifp & _TIF_IO_BITMAP) {
413-
/*
414-
* Do not touch the bitmap. Let the next bitmap using task
415-
* deal with the mess. Just make the io_bitmap_base invalid
416-
* by moving it outside the TSS limit so any subsequent I/O
417-
* access from user space will trigger a #GP.
418-
*
419-
* This is correct even when VMEXIT rewrites the TSS limit
420-
* to 0x67 as the only requirement is that the base points
421-
* outside the limit.
422-
*/
423-
tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET_INVALID;
440+
} else {
441+
tss_invalidate_io_bitmap(tss);
424442
}
425443
}
426444

@@ -634,7 +652,8 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p)
634652

635653
tifn = READ_ONCE(task_thread_info(next_p)->flags);
636654
tifp = READ_ONCE(task_thread_info(prev_p)->flags);
637-
switch_to_bitmap(next, tifp, tifn);
655+
656+
switch_to_bitmap(tifp);
638657

639658
propagate_user_return_notify(prev_p, next_p);
640659

0 commit comments

Comments
 (0)