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

Backend implementation #7

Merged
merged 21 commits into from
Aug 13, 2021
Merged

Backend implementation #7

merged 21 commits into from
Aug 13, 2021

Conversation

alindima
Copy link
Collaborator

@alindima alindima commented Jul 29, 2021

This PR implements the Rust IR and the logic for compiling it into BPF code.

In order for the design to be fully implemented, we need to add the JSON frontend, which will come in a future PR.

Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Copy link
Member

@andreeaflorescu andreeaflorescu left a comment

Choose a reason for hiding this comment

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

Did an initial pass. I still need to take a closer look at the tests.

src/backend/mod.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved

/// Comparison to perform when matching a condition.
#[derive(Clone, Debug, PartialEq)]
pub enum SeccompCmpOp {
Copy link
Member

Choose a reason for hiding this comment

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

nit: not all names follow the same convention. Some names have Seccomp as a prefix, others do not. I would drop Seccomp from all of them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reasoning behind this is so that we can easily distinguish the types that make up the IR (intermediate representation). All such types have Seccomp* embedded in their name.

This will make for more readable code once we add the JSON frontend types, those being named like JsonRule, JsonFilter, etc.

src/backend/rule.rs Show resolved Hide resolved
/// * `offset` - The given jump offset to the start of the next rule.
///
fn into_eq_bpf(self, offset: u8) -> Vec<sock_filter> {
let (msb, lsb) = self.split_value();
Copy link
Member

Choose a reason for hiding this comment

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

Having you looked into de-duplicating the into_ functions? I wonder if we could reduce the duplicated code while still keep the readability.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have, but I couldn't find a better way

bpf
})
.collect();
let chain_len: usize = chain.iter().map(std::vec::Vec::len).sum();
Copy link
Member

Choose a reason for hiding this comment

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

nit: Vec::len instead of std::vec::...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

let mut built_syscall = Vec::with_capacity(chain_len + 2);
built_syscall.push(BPF_JUMP(
BPF_JMP | BPF_JEQ | BPF_K,
// Safe because linux system call numbers are nowhere near the u32::MAX value.
Copy link
Member

Choose a reason for hiding this comment

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

Why do we define them as i64 then?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

because that's what libc expects for the syscall and that's what's used for the syscall numbers in rust's libc crate: https://docs.rs/libc/0.2.98/libc/constant.SYS_open.html.
I think it's better to have the cast here than duplicating it across countless system calls in user filters.

src/backend/bpf.rs Outdated Show resolved Hide resolved
src/backend/bpf.rs Outdated Show resolved Hide resolved
tests/integration_tests.rs Outdated Show resolved Hide resolved
@alindima alindima changed the base branch from master to main August 9, 2021 09:33
This struct will be used to parametrise the filters with
the target architecture they are compiled for.

The compilation logic will embed the architecture check
so that we make sure the BPF programs are not installed
on different architecures (which may have different system
call numbers).

Signed-off-by: alindima <alindima@amazon.com>
Module containing constants, types and helper functions
used in the BPF codegen.

The TargetArch::get_audit_value uses constants from the
BPF module for associating the TargetArch variant to the
right audit value used in the BPF compilation.

Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
src/lib.rs Outdated Show resolved Hide resolved
@likebreath
Copy link

@alindima Thank you for the great work. I finished a draft of integrating Cloud Hypervisor with your PR, and can confirm it works as expected with both SeccompAction Trap and Log.

Overall, I think the new interface (APIs and Types) are pretty good and cleaner. Admittedly it requires lots of changes due to the dramatic interface changes.

Only one comment/open-question: I wonder whether we should include some similar macros to make it easier (or more readable) to define complex SeccompCondition, for example the kvm ioctls rules in Cloud Hypervisor.

likebreath
likebreath previously approved these changes Aug 11, 2021
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
Renamed the filter_action to match_action and
the default_action to mismatch_action, to make them
slightly more expressive and easier to understand.

Signed-off-by: alindima <alindima@amazon.com>
Signed-off-by: alindima <alindima@amazon.com>
@alindima
Copy link
Collaborator Author

@alindima Thank you for the great work. I finished a draft of integrating Cloud Hypervisor with your PR, and can confirm it works as expected with both SeccompAction Trap and Log.

Thank you @likebreath for your work on integrating seccompiler in Cloud hypervisor, it's great to hear it works as expected!
I've taken a quick look over your draft PR and it looks good.

Overall, I think the new interface (APIs and Types) are pretty good and cleaner. Admittedly it requires lots of changes due to the dramatic interface changes.

Great to hear the interface has been indeed improved. Unfortunately it does require quite a lot of changes, but most of them are repetitive (and automatable with regex find&replace) and the one-time character of the changes I think make the effort worth it 😄

Only one comment/open-question: I wonder whether we should include some similar macros to make it easier (or more readable) to define complex SeccompCondition, for example the kvm ioctls rules in Cloud Hypervisor.

That is a good question. I've given the issue some thought. The two macros have a pretty small implementation and are quite an opinionated way of instantiating the objects. For example, other users of the library may not want to unwrap() the result of SeccompRule::new(vec![$($x),*]), like you are doing in your integration.

Given the above, I think it would be fine to keep those small macros in CH for now. What do you think?

Of course, we can revisit this choice later on if we come up with better abstractions that make the library easier to use for everybody.

@likebreath
Copy link

That is a good question. I've given the issue some thought. The two macros have a pretty small implementation and are quite an opinionated way of instantiating the objects. For example, other users of the library may not want to unwrap() the result of SeccompRule::new(vec![$($x),*]), like you are doing in your integration.

Right. That's something I wanted to improve hopefully at the time of landing the integration to CH (after landing your PR of course).

Given the above, I think it would be fine to keep those small macros in CH for now. What do you think?

This sounds good to me.

Of course, we can revisit this choice later on if we come up with better abstractions that make the library easier to use for everybody.

Agree. Improving abstractions over creating complex conditions definitely worth a revisit.

Copy link
Member

@andreeaflorescu andreeaflorescu left a comment

Choose a reason for hiding this comment

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

There are still a few std::u32::max calls that should be replaced with u32::max.

fn validate_seccomp_filter(
rules: Vec<(i64, Vec<SeccompRule>)>,
validation_fn: fn(),
should_fail: Option<bool>,
Copy link
Member

Choose a reason for hiding this comment

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

nit: It is a bit hard to understand how to use this parameter, specifically what is the meaning of None. We could define an enum in which we have 3 options (with some better names maybe):

enum Return {
    Errno(u32),
    NoErrno,
    NA,
}

@alindima alindima merged commit da5788d into rust-vmm:main Aug 13, 2021
@alindima alindima deleted the master branch October 7, 2021 06:20
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