Skip to content

Run Miri on profiling crates and fix miri issues + Introduce ddog_prof_Profile struct#235

Merged
morrisonlevi merged 9 commits into
mainfrom
dsn/miri-everywhere
Aug 30, 2023
Merged

Run Miri on profiling crates and fix miri issues + Introduce ddog_prof_Profile struct#235
morrisonlevi merged 9 commits into
mainfrom
dsn/miri-everywhere

Conversation

@danielsn

@danielsn danielsn commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

What does this PR do?

This PR is a followup to #225.

  1. It adds runs Miri on all feasible tests in profiling and profiling-ffi.
  2. It fixes UB in Miri found in some profiling-ffi tests
  3. It updates the CI action to run these tests on all future PRs.

While changing this code, we (DSN and Levi) felt it was better to make a few changes to make this easier to reason about safety:

  1. Introduce a ddog_prof_Profile struct that wraps a pointer to the Rust datadog_profiling::profile::Profile object, and return this from ddog_prof_Profile_new instead of ddog_prof_Profile*. This means you need to take the address for further operations e.g:
ddog_prof_Profile profile = ddog_prof_Profile_new(/*...*/);
ddog_prof_Profile_add(&profile, /*...*/);
ddog_prof_Profile_drop(&profile);
  1. Add ddog_prof_Profile_Result for errors which don't return anything on success, but return a ddog_Error when they fail. These functions have had their return type changed to use this result, and ddog_prof_Profile_UpscalingRuleAddResult and ddog_prof_Profile_AddResult were removed:
    • ddog_prof_Profile_add (previously ddog_prof_Profile_AddResult)
    • ddog_prof_Profile_set_endpoint (previously void)
    • ddog_prof_Profile_add_endpoint_count (previously void)
    • ddog_prof_Profile_add_upscaling_rule_poisson (previously ddog_prof_Profile_UpscalingRuleAddResult)
    • ddog_prof_Profile_add_upscaling_rule_proportional (previously ddog_prof_Profile_UpscalingRuleAddResult)
    • ddog_prof_Profile_reset (previously bool)

Motivation

Miri is a great way to find UB. We shouldn't have UB we can prevent.

Additional Notes

There was a subtle stacked-borrows violation cause by the ffi destructor taking an Option<&mut> instead of a *mut.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b3fb4e106c1d19183c831f38389f2658
shows how this plays out on a small example in the playground (use the tools/miri option to compare fails vs doesnt_fail.

How to test the change?

Update your code to account for the C API changes, but semantically nothing changed, so existing tests should work otherwise.

For Reviewers

  • If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from @DataDog/security-design-and-guidance.
  • This PR doesn't touch any of that.

@danielsn
danielsn requested a review from a team as a code owner August 29, 2023 20:31
@github-actions github-actions Bot added profiling Relates to the profiling* modules. ci-build labels Aug 29, 2023
@danielsn
danielsn force-pushed the dsn/miri-everywhere branch 6 times, most recently from c59432d to d873337 Compare August 29, 2023 20:39
@danielsn
danielsn force-pushed the dsn/miri-everywhere branch from d873337 to c152e1e Compare August 29, 2023 20:40
Comment thread profiling-ffi/src/profiles.rs Outdated
@danielsn
danielsn force-pushed the dsn/miri-everywhere branch from 9b0ac37 to 5149101 Compare August 29, 2023 20:53
@morrisonlevi morrisonlevi changed the title Run Miri on all of profiling and fix the issues it found Run Miri on profiling crates and fix miri issues, bump profiling version numbers Aug 29, 2023
@morrisonlevi morrisonlevi changed the title Run Miri on profiling crates and fix miri issues, bump profiling version numbers Run Miri on profiling crates and fix miri issues, bump version numbers Aug 30, 2023
@morrisonlevi morrisonlevi changed the title Run Miri on profiling crates and fix miri issues, bump version numbers Run Miri on profiling crates and fix miri issues Aug 30, 2023
// Leaving a null will help with double-free issues that can
// arise in C. Of course, it's best to never get there in the
// first place!
let raw = std::mem::replace(&mut self.inner, std::ptr::null_mut());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

since null is default, I think you can just std::mem::take here

@morrisonlevi morrisonlevi Aug 30, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I tried that first, but trait Default is apparently not implmented for *mut _!

/// The `profile` can be null, but if non-null it must point to a valid object
/// created by the Rust Global allocator.
/// The `profile` can be null, but if non-null it must point to a Profile
/// made by this module, which has not previously been dropped.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I like this comment better :)

@ivoanjo ivoanjo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 LGTM, thanks for making our ffi safer and more strictly checked :)

Comment on lines +18 to +19
# We need to disable isolation because
# "unsupported operation: `clock_gettime` with `REALTIME` clocks not available when isolation is enabled"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

❤️ for documenting this here :)

Comment thread examples/ffi/exporter.cpp
Comment on lines 42 to +45
const ddog_prof_Period period = {wall_time, 60};
std::unique_ptr<ddog_prof_Profile, Deleter> profile{
ddog_prof_Profile profile_data{
ddog_prof_Profile_new(sample_types, &period, nullptr)};
std::unique_ptr<ddog_prof_Profile, Deleter> profile{&profile_data};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: I wonder if such a simple example needs this kind of sophistication, e.g. maybe we could simplify this a bit?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I just fixed it up for the changes I made ^_^

I get that in C++, they want to have destructors run, rather than doing it manually. But obviously the C bindings don't have destructors.

I think we can maybe simplify this code, or acknowledge that we care more about C++ and try to also generate C++ headers and bindings, or something.

/// Resets all data except the sample types and period. Returns the
/// previous Profile on success.
pub fn reset(&mut self, start_time: Option<SystemTime>) -> Option<Profile> {
pub fn reset(&mut self, start_time: Option<SystemTime>) -> anyhow::Result<Profile> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: Is it still worth returning the old profile in this situation? As an API think, it's kinda weird -- when would we want to keep around the old profile after a reset?

@morrisonlevi morrisonlevi Aug 30, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I need to get this release moving so let's talk about that for v5.0. I think originally I was hoping to do something like:

let old = profile.reset();

// owned, not borrowed
let pprof_profile = pprof::Profile::from(old);

But that didn't work for whatever reason and forgot to revisit. Or may it's that since the structures don't match anymore (did at one time), there's not really value to take ownership of the profile::Profile, since it has to copy things anyway.

@morrisonlevi
morrisonlevi merged commit e1789d5 into main Aug 30, 2023
@morrisonlevi
morrisonlevi deleted the dsn/miri-everywhere branch August 30, 2023 14:59
ivoanjo added a commit to DataDog/dd-trace-rb that referenced this pull request Aug 31, 2023
@ivoanjo ivoanjo changed the title Run Miri on profiling crates and fix miri issues Run Miri on profiling crates and fix miri issues + Introduce ddog_prof_Profile struct Sep 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-build profiling Relates to the profiling* modules.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants