LibAFL icon indicating copy to clipboard operation
LibAFL copied to clipboard

Allow testcase syncing between fuzzers with different Input types

Open andreafioraldi opened this issue 3 years ago • 13 comments

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 avatar Oct 24 '22 08:10 andreafioraldi

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

andreafioraldi avatar Oct 24 '22 08:10 andreafioraldi

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

addisoncrump avatar Oct 24 '22 08:10 addisoncrump

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

andreafioraldi avatar Oct 24 '22 09:10 andreafioraldi

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).

addisoncrump avatar Oct 24 '22 09:10 addisoncrump

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.

andreafioraldi avatar Oct 24 '22 09:10 andreafioraldi

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

addisoncrump avatar Oct 24 '22 09:10 addisoncrump

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

addisoncrump avatar Oct 24 '22 09:10 addisoncrump

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

andreafioraldi avatar Oct 24 '22 10:10 andreafioraldi

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();
}

addisoncrump avatar Oct 24 '22 10:10 addisoncrump

Yes sorry I meant dyn Any, for the downcast

andreafioraldi avatar Oct 24 '22 11:10 andreafioraldi

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 avatar Oct 24 '22 11:10 addisoncrump

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.

addisoncrump avatar Oct 24 '22 13:10 addisoncrump

This is done, right?

domenukk avatar Apr 19 '23 14:04 domenukk