Skip to content

Commit 1e38da3

Browse files
committed
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]>
1 parent 7551b02 commit 1e38da3

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

fs/timerfd.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct timerfd_ctx {
4040
short unsigned settime_flags; /* to show in fdinfo */
4141
struct rcu_head rcu;
4242
struct list_head clist;
43+
spinlock_t cancel_lock;
4344
bool might_cancel;
4445
};
4546

@@ -112,7 +113,7 @@ void timerfd_clock_was_set(void)
112113
rcu_read_unlock();
113114
}
114115

115-
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
116+
static void __timerfd_remove_cancel(struct timerfd_ctx *ctx)
116117
{
117118
if (ctx->might_cancel) {
118119
ctx->might_cancel = false;
@@ -122,6 +123,13 @@ static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
122123
}
123124
}
124125

126+
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
127+
{
128+
spin_lock(&ctx->cancel_lock);
129+
__timerfd_remove_cancel(ctx);
130+
spin_unlock(&ctx->cancel_lock);
131+
}
132+
125133
static bool timerfd_canceled(struct timerfd_ctx *ctx)
126134
{
127135
if (!ctx->might_cancel || ctx->moffs != KTIME_MAX)
@@ -132,6 +140,7 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx)
132140

133141
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
134142
{
143+
spin_lock(&ctx->cancel_lock);
135144
if ((ctx->clockid == CLOCK_REALTIME ||
136145
ctx->clockid == CLOCK_REALTIME_ALARM) &&
137146
(flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
@@ -141,9 +150,10 @@ static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
141150
list_add_rcu(&ctx->clist, &cancel_list);
142151
spin_unlock(&cancel_lock);
143152
}
144-
} else if (ctx->might_cancel) {
145-
timerfd_remove_cancel(ctx);
153+
} else {
154+
__timerfd_remove_cancel(ctx);
146155
}
156+
spin_unlock(&ctx->cancel_lock);
147157
}
148158

149159
static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
@@ -400,6 +410,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
400410
return -ENOMEM;
401411

402412
init_waitqueue_head(&ctx->wqh);
413+
spin_lock_init(&ctx->cancel_lock);
403414
ctx->clockid = clockid;
404415

405416
if (isalarm(ctx))

0 commit comments

Comments
 (0)