
When the thread group leader changes during exec and the old leaders thread is reaped proc_flush_pid will flush the dentries for the entire process because the leader still has it's original pid. Fix this by exchanging the pids in an rcu safe manner, and wrapping the code to do that up in a helper exchange_tids. When I removed switch_exec_pids and introduced this behavior ind73d65293e
("[PATCH] pidhash: kill switch_exec_pids") there really was nothing that cared as flushing happened with the cached dentry and de_thread flushed both of them on exec. This lack of fully exchanging pids became a problem a few months later when I introduced48e6484d49
("[PATCH] proc: Rewrite the proc dentry flush on exit optimization"). Which overlooked the de_thread case was no longer swapping pids, and I was looking up proc dentries by task->pid. The current behavior isn't properly a bug as everything in proc will continue to work correctly just a little bit less efficiently. Fix this just so there are no little surprise corner cases waiting to bite people. -- Oleg points out this could be an issue in next_tgid in proc where has_group_leader_pid is called, and reording some of the assignments should fix that. -- Oleg points out this will break the 10 year old hack in __exit_signal.c > /* > * This can only happen if the caller is de_thread(). > * FIXME: this is the temporary hack, we should teach > * posix-cpu-timers to handle this case correctly. > */ > if (unlikely(has_group_leader_pid(tsk))) > posix_cpu_timers_exit_group(tsk); The code in next_tgid has been changed to use PIDTYPE_TGID, and the posix cpu timers code has been fixed so it does not need the 10 year old hack, so this should be safe to merge now. Link: https://lore.kernel.org/lkml/87h7x3ajll.fsf_-_@x220.int.ebiederm.org/ Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Acked-by: Oleg Nesterov <oleg@redhat.com> Fixes:48e6484d49
("[PATCH] proc: Rewrite the proc dentry flush on exit optimization"). Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
212 lines
6.1 KiB
C
212 lines
6.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_PID_H
|
|
#define _LINUX_PID_H
|
|
|
|
#include <linux/rculist.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/refcount.h>
|
|
|
|
enum pid_type
|
|
{
|
|
PIDTYPE_PID,
|
|
PIDTYPE_TGID,
|
|
PIDTYPE_PGID,
|
|
PIDTYPE_SID,
|
|
PIDTYPE_MAX,
|
|
};
|
|
|
|
/*
|
|
* What is struct pid?
|
|
*
|
|
* A struct pid is the kernel's internal notion of a process identifier.
|
|
* It refers to individual tasks, process groups, and sessions. While
|
|
* there are processes attached to it the struct pid lives in a hash
|
|
* table, so it and then the processes that it refers to can be found
|
|
* quickly from the numeric pid value. The attached processes may be
|
|
* quickly accessed by following pointers from struct pid.
|
|
*
|
|
* Storing pid_t values in the kernel and referring to them later has a
|
|
* problem. The process originally with that pid may have exited and the
|
|
* pid allocator wrapped, and another process could have come along
|
|
* and been assigned that pid.
|
|
*
|
|
* Referring to user space processes by holding a reference to struct
|
|
* task_struct has a problem. When the user space process exits
|
|
* the now useless task_struct is still kept. A task_struct plus a
|
|
* stack consumes around 10K of low kernel memory. More precisely
|
|
* this is THREAD_SIZE + sizeof(struct task_struct). By comparison
|
|
* a struct pid is about 64 bytes.
|
|
*
|
|
* Holding a reference to struct pid solves both of these problems.
|
|
* It is small so holding a reference does not consume a lot of
|
|
* resources, and since a new struct pid is allocated when the numeric pid
|
|
* value is reused (when pids wrap around) we don't mistakenly refer to new
|
|
* processes.
|
|
*/
|
|
|
|
|
|
/*
|
|
* struct upid is used to get the id of the struct pid, as it is
|
|
* seen in particular namespace. Later the struct pid is found with
|
|
* find_pid_ns() using the int nr and struct pid_namespace *ns.
|
|
*/
|
|
|
|
struct upid {
|
|
int nr;
|
|
struct pid_namespace *ns;
|
|
};
|
|
|
|
struct pid
|
|
{
|
|
refcount_t count;
|
|
unsigned int level;
|
|
spinlock_t lock;
|
|
/* lists of tasks that use this pid */
|
|
struct hlist_head tasks[PIDTYPE_MAX];
|
|
struct hlist_head inodes;
|
|
/* wait queue for pidfd notifications */
|
|
wait_queue_head_t wait_pidfd;
|
|
struct rcu_head rcu;
|
|
struct upid numbers[1];
|
|
};
|
|
|
|
extern struct pid init_struct_pid;
|
|
|
|
extern const struct file_operations pidfd_fops;
|
|
|
|
struct file;
|
|
|
|
extern struct pid *pidfd_pid(const struct file *file);
|
|
|
|
static inline struct pid *get_pid(struct pid *pid)
|
|
{
|
|
if (pid)
|
|
refcount_inc(&pid->count);
|
|
return pid;
|
|
}
|
|
|
|
extern void put_pid(struct pid *pid);
|
|
extern struct task_struct *pid_task(struct pid *pid, enum pid_type);
|
|
static inline bool pid_has_task(struct pid *pid, enum pid_type type)
|
|
{
|
|
return !hlist_empty(&pid->tasks[type]);
|
|
}
|
|
extern struct task_struct *get_pid_task(struct pid *pid, enum pid_type);
|
|
|
|
extern struct pid *get_task_pid(struct task_struct *task, enum pid_type type);
|
|
|
|
/*
|
|
* these helpers must be called with the tasklist_lock write-held.
|
|
*/
|
|
extern void attach_pid(struct task_struct *task, enum pid_type);
|
|
extern void detach_pid(struct task_struct *task, enum pid_type);
|
|
extern void change_pid(struct task_struct *task, enum pid_type,
|
|
struct pid *pid);
|
|
extern void exchange_tids(struct task_struct *task, struct task_struct *old);
|
|
extern void transfer_pid(struct task_struct *old, struct task_struct *new,
|
|
enum pid_type);
|
|
|
|
struct pid_namespace;
|
|
extern struct pid_namespace init_pid_ns;
|
|
|
|
/*
|
|
* look up a PID in the hash table. Must be called with the tasklist_lock
|
|
* or rcu_read_lock() held.
|
|
*
|
|
* find_pid_ns() finds the pid in the namespace specified
|
|
* find_vpid() finds the pid by its virtual id, i.e. in the current namespace
|
|
*
|
|
* see also find_task_by_vpid() set in include/linux/sched.h
|
|
*/
|
|
extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns);
|
|
extern struct pid *find_vpid(int nr);
|
|
|
|
/*
|
|
* Lookup a PID in the hash table, and return with it's count elevated.
|
|
*/
|
|
extern struct pid *find_get_pid(int nr);
|
|
extern struct pid *find_ge_pid(int nr, struct pid_namespace *);
|
|
|
|
extern struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,
|
|
size_t set_tid_size);
|
|
extern void free_pid(struct pid *pid);
|
|
extern void disable_pid_allocation(struct pid_namespace *ns);
|
|
|
|
/*
|
|
* ns_of_pid() returns the pid namespace in which the specified pid was
|
|
* allocated.
|
|
*
|
|
* NOTE:
|
|
* ns_of_pid() is expected to be called for a process (task) that has
|
|
* an attached 'struct pid' (see attach_pid(), detach_pid()) i.e @pid
|
|
* is expected to be non-NULL. If @pid is NULL, caller should handle
|
|
* the resulting NULL pid-ns.
|
|
*/
|
|
static inline struct pid_namespace *ns_of_pid(struct pid *pid)
|
|
{
|
|
struct pid_namespace *ns = NULL;
|
|
if (pid)
|
|
ns = pid->numbers[pid->level].ns;
|
|
return ns;
|
|
}
|
|
|
|
/*
|
|
* is_child_reaper returns true if the pid is the init process
|
|
* of the current namespace. As this one could be checked before
|
|
* pid_ns->child_reaper is assigned in copy_process, we check
|
|
* with the pid number.
|
|
*/
|
|
static inline bool is_child_reaper(struct pid *pid)
|
|
{
|
|
return pid->numbers[pid->level].nr == 1;
|
|
}
|
|
|
|
/*
|
|
* the helpers to get the pid's id seen from different namespaces
|
|
*
|
|
* pid_nr() : global id, i.e. the id seen from the init namespace;
|
|
* pid_vnr() : virtual id, i.e. the id seen from the pid namespace of
|
|
* current.
|
|
* pid_nr_ns() : id seen from the ns specified.
|
|
*
|
|
* see also task_xid_nr() etc in include/linux/sched.h
|
|
*/
|
|
|
|
static inline pid_t pid_nr(struct pid *pid)
|
|
{
|
|
pid_t nr = 0;
|
|
if (pid)
|
|
nr = pid->numbers[0].nr;
|
|
return nr;
|
|
}
|
|
|
|
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns);
|
|
pid_t pid_vnr(struct pid *pid);
|
|
|
|
#define do_each_pid_task(pid, type, task) \
|
|
do { \
|
|
if ((pid) != NULL) \
|
|
hlist_for_each_entry_rcu((task), \
|
|
&(pid)->tasks[type], pid_links[type]) {
|
|
|
|
/*
|
|
* Both old and new leaders may be attached to
|
|
* the same pid in the middle of de_thread().
|
|
*/
|
|
#define while_each_pid_task(pid, type, task) \
|
|
if (type == PIDTYPE_PID) \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define do_each_pid_thread(pid, type, task) \
|
|
do_each_pid_task(pid, type, task) { \
|
|
struct task_struct *tg___ = task; \
|
|
for_each_thread(tg___, task) {
|
|
|
|
#define while_each_pid_thread(pid, type, task) \
|
|
} \
|
|
task = tg___; \
|
|
} while_each_pid_task(pid, type, task)
|
|
#endif /* _LINUX_PID_H */
|