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

Allow testcase syncing between fuzzers with different Input types #858

Closed
andreafioraldi opened this issue Oct 24, 2022 · 13 comments
Closed
Labels
enhancement New feature or request

Comments

@andreafioraldi
Copy link
Member

andreafioraldi commented Oct 24, 2022

ATM, event managers can allow the share of testcases only if all the nodes have the same Input type.

Possible implementations are:

  • A Stage like DiskSyncStage that uses an LLMP channel instead of disk and custom serialize/deserialize lambas
  • A generalized version of the LLMP event manager in which instead of giving an Input type only we give two lambdas to define serialization to bytes and deserialization (different from Serde and more similar to TargetBytes, or we can even rely on input.target_bytes() instead of serialize)
  • An extension to the broker so that we have an architecture in which broker2broker can happen with different Input type
@andreafioraldi andreafioraldi added the enhancement New feature or request label Oct 24, 2022
@andreafioraldi
Copy link
Member Author

Also, we want something like "Sync pushing new testcases to the other fuzzer, never get new testcases from them" or viceversa

@addisoncrump
Copy link
Collaborator

It should be possible to a) send type information, b) select the specified type to deserialise, and c) if that type implements Into with T for the target-desired type, turn it into Some(T); if not, None

@andreafioraldi
Copy link
Member Author

andreafioraldi commented Oct 24, 2022

sending type info is not possible as typeid et similar requires 'static and Input is not, and even if we force Input to be static the type ids may change in different binaries as the synced fuzzers may be different binaries

@addisoncrump
Copy link
Collaborator

addisoncrump commented Oct 24, 2022

I'm not suggesting using generated type data, but specified type data. Use something like inventory to maintain a compile-time list of available types, storing a string and a Fn, then, when deserialising, find matching type name (if available) and apply deserialisation Fn to remainder. When serialising, send type name, then type data.

For then converting that type into whatever specified type we want, we need to make a trait (something like ConvertibleInput) which is required for all input types. Deserialisation function returns Box<dyn ConvertibleInput> instead of explicit type. ConvertibleInput provides a convert_to<T>(self) -> Option<T> method which attempts to convert to T if it can, None otherwise. The implementation of this convert_to<T> method which can be done with a trait (e.g., FromConvertibleInput<C: ConvertibleInput>) with a default function that returns None and specific implementations for types (e.g., impl<C: ConvertibleInput + HasTargetBytes> FromConvertible<C> for BytesInput).

@andreafioraldi
Copy link
Member Author

types registries (like inventory, that requires std and it is not compile-time but load-time as it uses constructors) require trait objects. We have a serializable trait objects registry, https://github.com/AFLplusplus/LibAFL/blob/main/libafl/src/bolts/serdeany.rs, but as I said this would force any Input to be 'static.

@addisoncrump
Copy link
Collaborator

I'm unclear as to why this would need to be static... At no point would this require TypeId.

@addisoncrump
Copy link
Collaborator

Also, for inventory, we don't need any specific type data. This would happen in dynamic dispatch.

@andreafioraldi
Copy link
Member Author

I'm unclear as to why this would need to be static... At no point would this require TypeId.

dyn Something forces Somthing to be static

@addisoncrump
Copy link
Collaborator

I don't think it does, no -- it requires it to be indirectly allocated, though.

trait Thing {
    fn do_thing(&self);
}

struct First;
struct Second;

impl Thing for First {
    fn do_thing(&self) {
        println!("First");
    }
}

impl Thing for Second {
    fn do_thing(&self) {
        println!("Second");
    }
}

fn main() {
    let first: Box<dyn Thing> = Box::new(First);
    let second: Box<dyn Thing> = Box::new(Second);

    first.do_thing();
    second.do_thing();
}

@andreafioraldi
Copy link
Member Author

Yes sorry I meant dyn Any, for the downcast

@addisoncrump
Copy link
Collaborator

Gotcha. I'll make a quick demo of the solution I'm suggesting to show you what I mean, which won't require any downcasting.

@addisoncrump
Copy link
Collaborator

Didn't implemented quite how I was originally thinking (I didn't consider that I would need a generic where I couldn't have one), but this works without 'static.

@domenukk
Copy link
Member

This is done, right?

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

No branches or pull requests

3 participants