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

Add support for no_std compilation. #74

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

tmroeder
Copy link

This adds a "std" feature that enables support for compilation with std. Without this feature, the library becomes compatible with #![no_std] crates.

By default, the library compiles with support for std types, but this can be disabled with --no-default-features.

This adds a "std" feature that enables support for compilation with std.
By default, the library compiles with support for std types, but this
can be disabled with --no-default-features.
@tmroeder
Copy link
Author

Note that this should address issue #38 as well.

@fitzgen
Copy link
Member

fitzgen commented Mar 15, 2021

See #62 (comment). What are you using arbitrary with? libfuzzer? If so, then you can use std. Even for primarily no-std crates, it is reasonable for the fuzz targets to use std (and they don't force the main crate itself to otherwise use std).

Would like to hear more about whether your use case actually requires arbitrary itself to be no-std.

@tmroeder
Copy link
Author

See #62 (comment). What are you using arbitrary with? libfuzzer? If so, then you can use std. Even for primarily no-std crates, it is reasonable for the fuzz targets to use std (and they don't force the main crate itself to otherwise use std).

Would like to hear more about whether your use case actually requires arbitrary itself to be no-std.

I started out that way in my use case.

The case is a large no_std crate where I want to derive Arbitrary on structs that are defined in that crate. Without no_std support, adding derive(Arbitrary) to a type in a no_std crate fails to compile. The exact use case is in structure-aware fuzzing: you want to fuzz an API with a function that takes as a parameter one of the structs defined in the crate. So, in the fuzz target, you define something like

#[derive(Arbitrary, Debug)]
enum Methods {
    FirstMethod {
        param1: usize,
        param2: MyStruct,
    },
   // ... other methods
}

where MyStruct is defined in the no_std crate. This requires MyStruct to satisfy the Arbitrary trait. My (perhaps incorrect) understanding is that this means that the no_std crate needs to have derive(Arbitrary) on MyStruct.

I realize that there's a workaround: use conditional compilation to turn on std in the crate when fuzzing, and only derive Arbitrary in that context. The value I see in this change is simplifying the task of adding fuzzing to these kinds of crates at the cost of some increased complexity in the arbitrary crate.

@fitzgen
Copy link
Member

fitzgen commented Mar 15, 2021

I realize that there's a workaround: use conditional compilation to turn on std in the crate when fuzzing, and only derive Arbitrary in that context. The value I see in this change is simplifying the task of adding fuzzing to these kinds of crates at the cost of some increased complexity in the arbitrary crate.

Yes, this is what I was about to suggest, and I do see the value in not requiring users to add a conditional std mode.

However, the benefit of this approach, over making arbitrary itself no-std compatible, is that it doesn't artificially constrain arbitrary's implementation, and we can continue to use std when implementing things inside arbitrary.

@bitwave
Copy link

bitwave commented Aug 19, 2021

I want to fuzz an embedded target, where no_std support comes in handy... ;)

@Manishearth
Copy link
Member

I realize that there's a workaround: use conditional compilation to turn on std in the crate when fuzzing, and only derive Arbitrary in that context. The value I see in this change is simplifying the task of adding fuzzing to these kinds of crates at the cost of some increased complexity in the arbitrary crate.

This isn't as much a workaround as it is the recommended way to do things, IMO. Arbitrary may need to use std types in its implementation so it's tricky to constrain ourselves to no_std. However, libFuzzer fuzzing always occurs in a std context, so you can use --features std as necessary.

We could potentially still have a std feature (on by default) which when disabled may affect the existence of Arbitrary impls for some no_std types. But I'm not sure if this is worth it.

@fitzgen
Copy link
Member

fitzgen commented Aug 19, 2021

Yep, you can use no_std crates from std crates, so arbitrary/libfuzzer-sys not being no_std doesn't constrain your no_std crate's implementation. At most you may have to add a std cargo feature when implementing Arbitrary for types inside your no_std crate, but this is something that cargo makes easy.

Re-upping my earlier comment from that other thread once again:

I personally can't think of any solid use case for no-std. Even if the system under test is no-std, the tests/fuzz targets don't have to be, and if your target has enough for libfuzzer, it should have enough for std. [...] Mostly it seems like adding no-std support would be a bunch of busy work that no one would end up actually using but would impose additional maintenance and CI overheads on us.


Before we close this issue, I think we should at least have some docs/examples of how to implement Arbitrary for types in a no_std crate, so that people don't just create a new issue for this again in the future.

@bitwave
Copy link

bitwave commented Aug 20, 2021

I updated the PR in my fork, to address the merging conflicts: https://github.com/bitwave/arbitrary/tree/no_std

@bitwave
Copy link

bitwave commented Nov 23, 2021

Any updates?

@bitwave
Copy link

bitwave commented Dec 26, 2021

Even for primarily no-std crates, it is reasonable for the fuzz targets to use std (and they don't force the main crate itself to otherwise use std).

Not if the fuzz target is a custom operating system & I have to use no_std and write mem alloc myself...

@pitdicker
Copy link

To add as a use case:
Chrono may be used as a dependency by some crate with default-features = false, features = "arbitrary". For chrono that implies no_std. Should the arbitrary dependency imply a dependency on std? Or should we put all implementations of Arbitrary behind an extra feature gate #[cfg(all(feature = "arbitrary", feature = "std"))]? I have currently gone with the latter in chronotope/chrono#1336.

It would be easier for a library if we did not need to care, and could specify arbitrary as dependency with default-features = "false" because all we need is to implement its trait.

And a second use case:
Suppose we have two different code paths, one that relies on the standard library and a more complex standlone one (as in chronotope/chrono#1163). It would be nice if there was a way to fuzz both, but that is not possible when arbitrary requires std.

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.

None yet

5 participants