Read the "real_parent" field of task_struct
John Wood
john.wood at gmx.com
Fri Sep 25 12:11:42 EDT 2020
Hi,
I'm working in a LSM that uses the task_alloc hook to do some work. This
hook needs to check the "real_parent" field hold by the task_struct
structure. I'm very confused since navigating the source code I see many
different ways to access this field. I don't understand why every method
is used in each case. So I don't know how to implement this access in my
LSM in a secure way.
The real_parent field is defined in the task_struct structure as:
struct task_struct __rcu *real_parent;
So, as far I can understand this pointer uses the "Read Copy Update"
feature.
Below I show some examples of different access:
--------------------------------------------------------------------------
Example 1
--------------------------------------------------------------------------
void proc_fork_connector(struct task_struct *task)
{
[...]
rcu_read_lock();
parent = rcu_dereference(task->real_parent);
ev->event_data.fork.parent_pid = parent->pid;
ev->event_data.fork.parent_tgid = parent->tgid;
rcu_read_unlock();
[...]
}
Here to access the real_parent field the code uses rcu_dereference inside
the rcu_read_lock/rcu_read_unlock block.
--------------------------------------------------------------------------
Example 2
--------------------------------------------------------------------------
static struct pid *
get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)
{
[...]
read_lock(&tasklist_lock);
[...]
if (task && task->real_parent == start &&
!(list_empty(&task->sibling))) {
[...]
read_unlock(&tasklist_lock);
[...]
}
Here to access the real_parent field the code reads the pointer directly
inside the read_lock/read_unlock block. No rcu block needed? Why is not
used the rcu_dereference function?
--------------------------------------------------------------------------
Example 3
--------------------------------------------------------------------------
struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
[...]
.real_parent = &init_task,
.parent = &init_task,
[...]
RCU_POINTER_INITIALIZER(real_cred, &init_cred),
RCU_POINTER_INITIALIZER(cred, &init_cred),
[...]
};
Here the initialization is directly. If the pointer is declared __rcu, the
assigment to the real_parent should be with RCU_POINTER_INITIALIZER macro?
--------------------------------------------------------------------------
Example 4
--------------------------------------------------------------------------
static void forget_original_parent(struct task_struct *father,
struct list_head *dead)
{
[...]
RCU_INIT_POINTER(t->real_parent, reaper);
[...]
}
Here the initialization uses the RCU_INIT_POINTER macro. It's not directly.
--------------------------------------------------------------------------
Example 5
--------------------------------------------------------------------------
SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
{
[...]
rcu_read_lock();
/* From this point forward we keep holding onto the tasklist lock
* so that our parent does not change from under us. -DaveM
*/
write_lock_irq(&tasklist_lock);
[...]
if (same_thread_group(p->real_parent, group_leader)) {
[...]
write_unlock_irq(&tasklist_lock);
rcu_read_unlock();
[...]
}
Here to access the real_parent field the code reads the pointer directly
inside the write_lock_irq/write_unlock_irq block nested in a rcu_read_lock/
rcu_read_unlock block.
--------------------------------------------------------------------------
Example 6
--------------------------------------------------------------------------
long keyctl_session_to_parent(void)
{
[...]
rcu_read_lock();
write_lock_irq(&tasklist_lock);
[...]
parent = rcu_dereference_protected(me->real_parent,
lockdep_is_held(&tasklist_lock));
[...]
write_unlock_irq(&tasklist_lock);
rcu_read_unlock();
[...]
}
But here to access the real_parent field the code uses rcu_dereference_*
inside the write_lock_irq/write_unlock_irq block nested in a rcu_read_lock/
rcu_read_unlock block. The nested blocks are the sames that in the example 5
but the access is not directly, it uses rcu_dereference_*. Why?
Extracted from the documentation:
[1] The variant rcu_dereference_protected() can be used outside of an RCU
read-side critical section as long as the usage is protected by locks
acquired by the update-side code. This variant avoids the lockdep warning
that would happen when using (for example) rcu_dereference() without
rcu_read_lock() protection. Using rcu_dereference_protected() also has
the advantage of permitting compiler optimizations that rcu_dereference()
must prohibit. The rcu_dereference_protected() variant takes a lockdep
expression to indicate which locks must be acquired by the caller. If the
indicated protection is not provided, a lockdep splat is emitted.
Moreover, why the rcu_dereference_protected is used under a rcu block?
There are more examples but are similars to the ones showed. So my question
is how to read the "real_parent" field correctly. If I can understand all
the above examples I think I will have the knowledge to implement my LSM in
a correct way.
Any help that points me to the right direction will be greatly apreciated.
Some rules to know when and why use a method or another are welcome.
Thanks in advance. Regards,
John Wood
More information about the Kernelnewbies
mailing list