Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kernel: validate kernel objects #834

Closed
wants to merge 2 commits into from

Conversation

andrewboie
Copy link
Contributor

This will be used in conjunction with the kernel/userspace
privilege split, when a user thread makes a system call with
a kernel object as a parameter, the validity of the kernel
object pointer passed in will be confirmed, and cause a kernel
oops if it wasn't valid.

This is done by randomly generating a set of XOR keys for each
kernel object type, and when an object is initialized its
pointer value will be XOR'd with the key and stored within
the object itself. When an object is being validated,
XORing this value again with the key for the object type should
pop out the original pointer again.

We validate most, but not all kernel objects. We currently
omit k_mbox, k_queue, k_mem_pool as they appear to need some
redesign before we can use them from userspace due to how they
store data. JIRAs have been opened for each of these.

Issue: ZEP-2187
Signed-off-by: Andrew Boie andrew.p.boie@intel.com

@andrewboie
Copy link
Contributor Author

andrewboie commented Jul 18, 2017

I want to to turn this on by default for all the QEMU targets, but waiting on some fixes for GH-3830 before I can do that.

Copy link
Collaborator

@lpereira lpereira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have to review this thoroughly, but there are some nits.

include/kernel.h Outdated
@@ -2136,6 +2191,7 @@ extern int k_delayed_work_cancel(struct k_delayed_work *work);
*/
static inline void k_work_submit(struct k_work *work)
{
_k_object_validate(work, K_OBJ_WORK);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validates work twice. (It should only call _k_object_validate() before dereferencing the kernel object; if it's just passing it forward, let the other function validate it.)

#ifdef CONFIG_PRINTK
char *otype_to_str(enum k_objects otype)
{
switch (otype) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a const char *otype_str[] array will generate tighter code.

@andrewboie
Copy link
Contributor Author

rebased and addressed @lpereira review comments


initialized = 1;

for (i = 0; i < K_OBJ_LAST; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't see why you can't fold these into the same loop. Initialize the metadata, then loop over the defined objects.

Andrew Boie added 2 commits July 19, 2017 12:52
This will be used in conjunction with the kernel/userspace
privilege split, when a user thread makes a system call with
a kernel object as a parameter, the validity of the kernel
object pointer passed in will be confirmed, and cause a kernel
oops if it wasn't valid.

This is done by randomly generating a set of XOR keys for each
kernel object type, and when an object is initialized its
pointer value will be XOR'd with the key and stored within
the object itself. When an object is being validated,
XORing this value again with the key for the object type should
pop out the original pointer again.

We validate most, but not all kernel objects. We currently
omit k_mbox, k_queue, k_mem_pool as they appear to need some
redesign before we can use them from userspace due to how they
store data. JIRAs have been opened for each of these.

Issue: ZEP-2187
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This will help catch bugs when running sanitycheck if an
application or subsystem tries to make kernel calls on
something that is not a valid kernel object.

This adds a bit of overhead but we don't gather performance
metrics in QEMU anyway, it's too dependent on the
characteristics of the host system.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
@andrewboie
Copy link
Contributor Author

Addressed @andyross feedback and added a more helpful error message if an app defines a kernel object in non-kernel memory.

struct object_metadata *o = &ometa[i];

#if CONFIG_RANDOM_HAS_DRIVER
o->xor_key = sys_rand32_get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this, we don't want a random number here. Think of the requirements:

  1. It should be "white" relative to the the space of pointers and the other xor_key values. A change in any bit of the pointer should be ~50% likely to invalidate the match. It shouldn't be "too close" to the other keys.

  2. It should not be the same as any other xor_key

A RNG will meet those only statistically. And worse, because it's runtime-dependent (on systems with hardware entropy anyway) we're pretty much guaranteed that eventually a system will boot up having hit the birthday jackpot and have colliding (or even partially colliding, which is still bad) keys.

We can do better by precomputing it at build time. A quick python script that does the RNG and tests for validity before emitting C code to define the ometa[] array would be easy and simplify the boot code (heh) to boot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andyross good point, but I fear static keys only makes the situation worse, as soon as someone discovers the set of static XOR keys by just reading them out the flash, or JTAG, or whatever, that will enable every script kiddie to hack every single device out there.

not seeing a good way out of this :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, yeah. It's not really a security feature anyway. But the complaint above was about robustness, it's possible to boot in a configuration where the validity check doesn't work right.

FWIW: you could do both: keep a single/global per-boot randomized key and a per-type value and xor with both (potentially pre-computing the xor_key values in place, assuming they're in RAM as they are now). Probably not worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we admit that this is totally insecure we might as well not have random numbers at all, and just use the loop counter (basically just the !CONFIG_RANDOM_HAS_DRIVER case) maybe with some math done on it to ensure not too close to other keys.

i guess let this patch be a prototype of the pointer-based API model. i'm going to prototype in another patch what a descriptor-based validation mechanism would look like, I think it will at least inform the discussion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do multiple layers very easily
You could have keys per thread, per boot and per type ... and they all have to match in the right order. That significantly increases the difficulty of making a guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@inakypg that falls apart if you need to grant more than one thread access. if you grant exactly one thread access, sure you can just xor its struct k_thread pointer in. but multiple threads, not gonna work, you can't just XOR in all their pointers in. in practice we are going to need to grant multiple threads access to some object all the time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewboie
Copy link
Contributor Author

Investigating a different approach. Closing for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants