Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/validate-kernel-commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Validate Kernel Commits

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
check:
uses: ctrliq/kernel-src-tree/.github/workflows/validate-kernel-commits.yml@main
secrets: inherit
28 changes: 20 additions & 8 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,17 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
perf_event_comm(tsk, exec);
}

static void posixtimer_exec(struct task_struct *me)
{
#ifdef CONFIG_POSIX_TIMERS
spin_lock_irq(&me->sighand->siglock);
posix_cpu_timers_exit(me);
spin_unlock_irq(&me->sighand->siglock);
exit_itimers(me);
flush_itimer_signals();
#endif
}

/*
* Calling this is the point of no return. None of the failures will be
* seen by userspace since either the process is already taking a fatal
Expand Down Expand Up @@ -1239,6 +1250,15 @@ int begin_new_exec(struct linux_binprm * bprm)
if (retval)
goto out;

/*
* This must be done here to ensure that POSIX CPU timers which were
* armed on the current task are dequeued from me::posix_cputimers.
* Otherwise in case of a TID switch the deletion of the related POSIX
* timer would not remove an enqueued timer because the TID lookup
* of the old TID fails.
*/
posixtimer_exec(me);

/*
* Cancel any io_uring activity across execve
*/
Expand Down Expand Up @@ -1277,14 +1297,6 @@ int begin_new_exec(struct linux_binprm * bprm)
if (retval)
goto out_unlock;

#ifdef CONFIG_POSIX_TIMERS
spin_lock_irq(&me->sighand->siglock);
posix_cpu_timers_exit(me);
spin_unlock_irq(&me->sighand->siglock);
exit_itimers(me);
flush_itimer_signals();
#endif

/*
* Make the signal table private.
*/
Expand Down
11 changes: 6 additions & 5 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,13 @@ void release_task(struct task_struct *p)
put_pid(thread_pid);
release_thread(p);
/*
* This task was already removed from the process/thread/pid lists
* and lock_task_sighand(p) can't succeed. Nobody else can touch
* ->pending or, if group dead, signal->shared_pending. We can call
* flush_sigqueue() lockless.
* This task was already removed from the process/thread/pid lists and
* lock_task_sighand(p) can't succeed. If it's the group leader then
* flush tsk->signal->shared_pending. tsk->pending has been flushed
* already in exit_signals(). Nothing else can touch
* signal->shared_pending anymore, so flush_sigqueue() can be invoked
* lockless.
*/
flush_sigqueue(&p->pending);
if (thread_group_leader(p))
flush_sigqueue(&p->signal->shared_pending);

Expand Down
16 changes: 10 additions & 6 deletions kernel/futex/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1799,14 +1799,18 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags)
}

if (!mm->futex_ref) {
unsigned int __percpu *ref = alloc_percpu(unsigned int);

if (!ref)
return -ENOMEM;

/*
* This will always be allocated by the first thread and
* therefore requires no locking.
* Tasks sharing the mm can run this concurrently, so take the
* initial reference before publishing the counter.
*/
mm->futex_ref = alloc_percpu(unsigned int);
if (!mm->futex_ref)
return -ENOMEM;
this_cpu_inc(*mm->futex_ref); /* 0 -> 1 */
this_cpu_inc(*ref); /* 0 -> 1 */
if (cmpxchg(&mm->futex_ref, NULL, ref))
free_percpu(ref);
}

fph = kvzalloc(struct_size(fph, queues, hash_slots),
Expand Down
121 changes: 88 additions & 33 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,18 +474,42 @@ static void __sigqueue_free(struct sigqueue *q)
kmem_cache_free(sigqueue_cachep, q);
}

void flush_sigqueue(struct sigpending *queue)
/*
* flush_sigqueue_list() can only be invoked without holding sighand::siglock in
* the following cases:
*
* 1) When flushing task::pending _after_ setting task::flags PF_EXITING
*
* All functions which try to send a signal to @task will observe PF_EXITING
* and drop the signal.
*
* 2) When flushing task::signal::shared_pending _after_ the last task in a
* thread group was unhashed and task::sighand is NULL.
*
* Nothing can queue a signal anymore because sighand is NULL.
*/
static void flush_sigqueue_list(struct list_head *head)
{
struct sigqueue *q;
struct sigqueue *q, *tmp;

sigemptyset(&queue->signal);
while (!list_empty(&queue->list)) {
q = list_entry(queue->list.next, struct sigqueue , list);
list_for_each_entry_safe(q, tmp, head, list) {
list_del_init(&q->list);
__sigqueue_free(q);
}
}

void flush_sigqueue(struct sigpending *queue)
{
sigemptyset(&queue->signal);
flush_sigqueue_list(&queue->list);
}

static void sigqueue_dequeue_pending(struct sigpending *queue, struct list_head *head)
{
sigemptyset(&queue->signal);
list_splice_init(&queue->list, head);
}

/*
* Flush all pending signals for this kthread.
*/
Expand Down Expand Up @@ -1038,6 +1062,21 @@ static inline bool legacy_queue(struct sigpending *signals, int sig)
return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
}

/*
* When PF_EXITING is set the task is on the way out and has t::pending
* flushed already. Prevent queueing of PIDTYPE_PID signals as they would
* be leaked.
*/
static inline bool task_can_queue_signal(struct task_struct *t, enum pid_type type)
{
lockdep_assert_held(&t->sighand->siglock);

if (!(t->flags & PF_EXITING))
return true;

return type != PIDTYPE_PID;
}

static int __send_signal_locked(int sig, struct kernel_siginfo *info,
struct task_struct *t, enum pid_type type, bool force)
{
Expand All @@ -1049,6 +1088,10 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info,
lockdep_assert_held(&t->sighand->siglock);

result = TRACE_SIGNAL_IGNORED;

if (!task_can_queue_signal(t, type))
goto ret;

if (!prepare_signal(sig, t, force))
goto ret;

Expand Down Expand Up @@ -1967,18 +2010,33 @@ static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t,
*
* Where type is not PIDTYPE_PID, signals must be delivered to the
* process. In this case, prefer to deliver to current if it is in
* the same thread group as the target process, which avoids
* unnecessarily waking up a potentially idle task.
* the same thread group as the target process and its sighand is
* stable, which avoids unnecessarily waking up a potentially idle task.
*/
static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
{
struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type);

if (t && tmr->it_pid_type != PIDTYPE_PID && same_thread_group(t, current))
if (t && tmr->it_pid_type != PIDTYPE_PID &&
same_thread_group(t, current) && !(current->flags & PF_EXITING))
t = current;
return t;
}

/*
* Find the target task for the POSIX timer signal and prevent that a
* PIDTYPE_PID signal is queued on a task which has PF_EXITING set.
*/
static inline struct task_struct *posixtimer_get_unignore_target(struct k_itimer *tmr)
{
struct task_struct *t = posixtimer_get_target(tmr);

if (t && task_can_queue_signal(t, tmr->it_pid_type))
return t;

return NULL;
}

void posixtimer_send_sigqueue(struct k_itimer *tmr)
{
struct sigqueue *q = &tmr->sigq;
Expand All @@ -1996,6 +2054,9 @@ void posixtimer_send_sigqueue(struct k_itimer *tmr)
if (!likely(lock_task_sighand(t, &flags)))
return;

if (!task_can_queue_signal(t, tmr->it_pid_type))
goto unlock;

/*
* Update @tmr::sigqueue_seq for posix timer signals with sighand
* locked to prevent a race against dequeue_signal().
Expand Down Expand Up @@ -2087,6 +2148,7 @@ void posixtimer_send_sigqueue(struct k_itimer *tmr)
result = TRACE_SIGNAL_DELIVERED;
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock:
unlock_task_sighand(t, &flags);
}

Expand Down Expand Up @@ -2142,7 +2204,7 @@ static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
* has exited by now, drop the reference count.
*/
guard(rcu)();
target = posixtimer_get_target(tmr);
target = posixtimer_get_unignore_target(tmr);
if (target)
posixtimer_queue_sigqueue(&tmr->sigq, target, tmr->it_pid_type);
else
Expand Down Expand Up @@ -3124,44 +3186,37 @@ static void retarget_shared_pending(struct task_struct *tsk, sigset_t *which)

void exit_signals(struct task_struct *tsk)
{
LIST_HEAD(sigq_list);
int group_stop = 0;
sigset_t unblocked;

/*
* @tsk is about to have PF_EXITING set - lock out users which
* expect stable threadgroup.
* expect a stable threadgroup.
*/
cgroup_threadgroup_change_begin(tsk);

if (thread_group_empty(tsk) || (tsk->signal->flags & SIGNAL_GROUP_EXIT)) {
scoped_guard(spinlock_irq, &tsk->sighand->siglock) {
sched_mm_cid_exit_signals(tsk);
tsk->flags |= PF_EXITING;
cgroup_threadgroup_change_end(tsk);
return;
}

spin_lock_irq(&tsk->sighand->siglock);
/*
* From now this task is not visible for group-wide signals,
* see wants_signal(), do_signal_stop().
*/
sched_mm_cid_exit_signals(tsk);
tsk->flags |= PF_EXITING;
sigqueue_dequeue_pending(&tsk->pending, &sigq_list);

cgroup_threadgroup_change_end(tsk);
if (task_sigpending(tsk) && !thread_group_empty(tsk) &&
!(tsk->signal->flags & SIGNAL_GROUP_EXIT)) {
sigset_t unblocked = tsk->blocked;

if (!task_sigpending(tsk))
goto out;
signotset(&unblocked);
retarget_shared_pending(tsk, &unblocked);

unblocked = tsk->blocked;
signotset(&unblocked);
retarget_shared_pending(tsk, &unblocked);
if (unlikely(tsk->jobctl & JOBCTL_STOP_PENDING) &&
task_participate_group_stop(tsk))
group_stop = CLD_STOPPED;
}
}

if (unlikely(tsk->jobctl & JOBCTL_STOP_PENDING) &&
task_participate_group_stop(tsk))
group_stop = CLD_STOPPED;
out:
spin_unlock_irq(&tsk->sighand->siglock);
cgroup_threadgroup_change_end(tsk);

flush_sigqueue_list(&sigq_list);

/*
* If group stop has completed, deliver the notification. This
Expand Down
Loading