All of lore.kernel.org
 help / color / mirror / Atom feed
From: John Wood <john.wood@gmx.com>
To: kernelnewbies@kernelnewbies.org
Cc: John Wood <john.wood@gmx.com>
Subject: Read the "real_parent" field of task_struct
Date: Fri, 25 Sep 2020 18:11:42 +0200	[thread overview]
Message-ID: <20200925161142.GA2857@ubuntu> (raw)

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


_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@kernelnewbies.org
https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

             reply	other threads:[~2020-09-29 18:18 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-25 16:11 John Wood [this message]
2020-09-30 11:59 ` Read the "real_parent" field of task_struct Valdis Klētnieks
2020-10-01 17:49   ` John Wood
2020-10-02  0:29     ` Valdis Klētnieks
2020-10-02 16:59       ` John Wood
2020-10-08 17:05         ` John Wood

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200925161142.GA2857@ubuntu \
    --to=john.wood@gmx.com \
    --cc=kernelnewbies@kernelnewbies.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.