Skip to content

Commit 5c32396

Browse files
KAGA-KOKOLinuxMinion
authored andcommitted
timerfd: Protect the might cancel mechanism proper
The handling of the might_cancel queueing is not properly protected, so parallel operations on the file descriptor can race with each other and lead to list corruptions or use after free. Protect the context for these operations with a seperate lock. The wait queue lock cannot be reused for this because that would create a lock inversion scenario vs. the cancel lock. Replacing might_cancel with an atomic (atomic_t or atomic bit) does not help either because it still can race vs. the actual list operation. Reported-by: Dmitry Vyukov <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: "[email protected]" Cc: syzkaller <[email protected]> Cc: Al Viro <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/alpine.DEB.2.20.1701311521430.3457@nanos Signed-off-by: Thomas Gleixner <[email protected]> (cherry picked from commit 1e38da3) Orabug: 26673877 CVE: CVE-2017-10661 Signed-off-by: Tim Tianyang Chen <[email protected]> Reviewed-by: Jack Vogel <[email protected]>
1 parent 275f8cb commit 5c32396

File tree

1 file changed

+16
-3
lines changed

1 file changed

+16
-3
lines changed

fs/timerfd.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ struct timerfd_ctx {
4141
struct rcu_head rcu;
4242
struct list_head clist;
4343
bool might_cancel;
44+
#ifndef __GENKSYMS__
45+
spinlock_t cancel_lock;
46+
#endif
4447
};
4548

4649
static LIST_HEAD(cancel_list);
@@ -112,7 +115,7 @@ void timerfd_clock_was_set(void)
112115
rcu_read_unlock();
113116
}
114117

115-
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
118+
static void __timerfd_remove_cancel(struct timerfd_ctx *ctx)
116119
{
117120
if (ctx->might_cancel) {
118121
ctx->might_cancel = false;
@@ -122,6 +125,13 @@ static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
122125
}
123126
}
124127

128+
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
129+
{
130+
spin_lock(&ctx->cancel_lock);
131+
__timerfd_remove_cancel(ctx);
132+
spin_unlock(&ctx->cancel_lock);
133+
}
134+
125135
static bool timerfd_canceled(struct timerfd_ctx *ctx)
126136
{
127137
if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX)
@@ -132,6 +142,7 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx)
132142

133143
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
134144
{
145+
spin_lock(&ctx->cancel_lock);
135146
if ((ctx->clockid == CLOCK_REALTIME ||
136147
ctx->clockid == CLOCK_REALTIME_ALARM) &&
137148
(flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
@@ -141,9 +152,10 @@ static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
141152
list_add_rcu(&ctx->clist, &cancel_list);
142153
spin_unlock(&cancel_lock);
143154
}
144-
} else if (ctx->might_cancel) {
145-
timerfd_remove_cancel(ctx);
155+
} else {
156+
__timerfd_remove_cancel(ctx);
146157
}
158+
spin_unlock(&ctx->cancel_lock);
147159
}
148160

149161
static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
@@ -395,6 +407,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
395407
return -ENOMEM;
396408

397409
init_waitqueue_head(&ctx->wqh);
410+
spin_lock_init(&ctx->cancel_lock);
398411
ctx->clockid = clockid;
399412

400413
if (isalarm(ctx))

0 commit comments

Comments
 (0)