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

Refactor of Qemu configuration #2707

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open

Refactor of Qemu configuration #2707

wants to merge 25 commits into from

Conversation

rmalmain
Copy link
Collaborator

No description provided.


#[test]
#[cfg(feature = "usermode")]
fn usermode() {
let program = "/bin/pwd";
let qemu = Qemu::builder().program("/bin/pwd").build().unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

Doens't the old API look better here? Can't we just keep Qemu::builder() and .build() then calls Qemu::init_whatever?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's true, but the thing is that we need to separate the configuration step from the init step, and the builder generated by typed-builder is not made for this, it's a pain to move around

Copy link
Member

Choose a reason for hiding this comment

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

IMHO it's more important to have a good API than to have nice code "on the inside".
This is already what you're using (right?) idanarye/rust-typed-builder#80

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

from the user's point of view, i think separating configuration from actual initialization is not so bad as well.
it makes more clear what the configuration part is and where qemu is actually getting initialized.

hiding something like qemu initialization in a From implementation doesn't sound good imho

Copy link
Member

@domenukk domenukk Nov 22, 2024

Choose a reason for hiding this comment

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

No it's really bad for users. We have builders everywhere in the codebase and they all work the same. There's no reason this shoudl work differently. You can still pass around an un-built builder

Copy link
Member

@domenukk domenukk Dec 3, 2024

Choose a reason for hiding this comment

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

Why not migrate all uses of Qemu to Emulator directly? I (as a user) don't get the split between those two

I mean that's probably beyond the scope of this PR...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@domenukk the idea of Qemu is to have a 0-sized object giving a slightly higher-level interface to QEMU (basically making it more convenient to call to QEMU functions and providing a more rusty api), that can only be obtained when QEMU is correctly initialized.
i use this trick a lot to give a Qemu as parameter for 0 cost.
also (i may be wrong), i think it's nice to let the possibility to use QEMU without all the things in Emulator and still provide a usable API.

in practice, i think you're right and most people will not want to use Qemu alone at all. the separation mostly exists to make it simpler to separate things and make it more clear what is specific to QEMU and what is higher-level stuff we introduce. A possible thing we can try is to move Qemu and QemuHooks to libafl_qemu_sys? at least it would remove the confusion between Emulator and Qemu for users.

@Marcondiro i thought about this, but i think it will be quite heavy to do in practice because rust-typed-builder adds up many generics internally that would be difficult to annotate explicitly.

Copy link
Member

Choose a reason for hiding this comment

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

Why not make a builder for emulator instead, and remove qemu from the user-facing APIs then?

Copy link
Member

Choose a reason for hiding this comment

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

Also, you're not married to typed_builder if it dropping it makes the API cleaner

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

QemuConfig is quite big, not using something like typed_builder would be a lot of work and make it less maintainable. idk of any other typed builder like this one.
EmulatorBuilder already exists btw, it's used to set things like the modules, snapshot engine, etc...
QemuConfig is just a programmatic way to set the cli of QEMU. if we continue to use the derive macro for getting qemu cli from QemuConfig, we need all the fields of the builder to be an option of the QEMU cli, hence the current split.

@Marcondiro

This comment was marked as outdated.

@Marcondiro

This comment was marked as resolved.

* back to one QEMU init function
* other small things
* qemu is now a parameter to EmulatorModule callbacks and most function hooks.
* EmulatorModules is initialized before QEMU is initialized.
@rmalmain
Copy link
Collaborator Author

some things we should do at some point:

  • i think we should make Emulator an argument to EmulatorModule and remove the self reference. otherwise (it's an old problem) each EmulatorModule has a double mut reference to itself (one through &mut self and one through EmulatorModules).
  • we should find a better way to type hooks callbacks. for now they are declared 2 times (both in rust and c side) and can easily get messed up. also, it's quite verbose to create new hooks. imho we should create some kind of builder on c side and simplify the rust side with more macros.

* continue to propagate qemu argument as hook first parameter
* use pre_syscall* and post_syscall* everywhere
* fix some clippy stuff
…d use the module interface instead.

* adapt qemu_launcher example to fully work with emulator, since qemu must now be initialized by emulator.
@rmalmain
Copy link
Collaborator Author

this pr has an important breaking change: qemu cannot be initialized independently from emulator. if emulator is needed, qemu must be initialized by emulator (before it was possible to initialize qemu and give it to emulator).
this is because we now have callbacks for things happening before qemu gets initialized, so obviously we cannot initialize it before.
i changed the way asan stuff gets initialized, it should be more natural (i removed the custom init of qemu for asan and moved it to module callbacks).

@Marcondiro
Copy link
Contributor

@rmalmain can you merge main? thanks

@tokatoka
Copy link
Member

tokatoka commented Dec 5, 2024

Can you write into MIGRATION.md about what you changed in this PR right before you merge this?

@rmalmain
Copy link
Collaborator Author

rmalmain commented Dec 5, 2024

i'm still editing stuff, i'll write when the pr is over

@rmalmain
Copy link
Collaborator Author

rmalmain commented Dec 5, 2024

new proposal for the emulator builder:

  • do not use TypedBuilder anymore, and replace it with a 'runtime' builder. otherwise i cannot really use the builder around because of the generics problem.
  • add qemu_config method to EmulatorBuilder that takes a closure as argument with the current qemu configurator as argument and must return the new configurator. that way, ppl can configure qemu from emulator without too much trouble. not sure it's the best solution, but i think it's a bit more usable like this.

opinion @domenukk @Marcondiro ?

@Marcondiro
Copy link
Contributor

Marcondiro commented Dec 6, 2024

Hey, probably a stupid question, wasn't it enough to add an empty pub(crate) new() -> Self for Qemu so that the QemuConfig could have remained (almost) unchanged and the init fn called at the right moment by emu?

(Also, should we make Qemu::init less pub anyway now?)

@rmalmain
Copy link
Collaborator Author

rmalmain commented Dec 6, 2024

Hey, probably a stupid question, wasn't it enough to add an empty pub(crate) new() -> Self for Qemu so that the QemuConfig could have remained (almost) unchanged and the init fn called at the right moment by emu?

i'm not sure to understand. do you mean QemuBuilder in the original implementation should have returned a Qemu from a new (empty) function, and init called later on by EmulatorBuilder?

(Also, should we make Qemu::init less pub anyway now?)

if Qemu was not pub, it would be much more simple indeed. i'm trying to keep it pub because i think there could be usecases for Qemu without an Emulator. what i think we can do though is to move it (with QemuHooks) to libafl_qemu_sys to make it less confusing for users what should be used in most cases.

@Marcondiro
Copy link
Contributor

i'm not sure to understand. do you mean QemuBuilder in the original implementation should have returned a Qemu from a new (empty) function, and init called later on by EmulatorBuilder?

yeah exactly, in the From that converts QemuConfig -> Qemu we can simply call a dummy Qemu::new() and then (a modified) Qemu::init() -> Result<(), ...> in Emu

@rmalmain
Copy link
Collaborator Author

rmalmain commented Dec 6, 2024

yeah exactly, in the From that converts QemuConfig -> Qemu we can simply call a dummy Qemu::new() and then (a modified) Qemu::init() -> Result<(), ...> in Emu

hum i don't think we want to do that. it gives access to all the Qemu functions that only make sense after init is called. i think we really want to give Qemu when it's ready to be fully usable.

@Marcondiro
Copy link
Contributor

Marcondiro commented Dec 6, 2024

Ok got it thanks!

If the Qemu "direct" initialization is gonna be a more "advanced", hidden and less used API, I would care more about the EmulatorBuilder API than the Qemu's init/builder/build/init_with_config. Instead to me it looks like we are doing the opposite, the closure thing is not very handy imho...

I think as a user of EmulatorBuilder I'd like to just use .qemu_config(my_qemu_config), and that's it.
Would this be doable?
Like for instance making .qemu_config take T: Into<QemuParam> and implementing From<QemuConfig> (or From<QemuConfigBuilder>) and From<Vec[String]> for QemuParam

Sorry if I'm missing something :D

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