This is an archive of the discontinued LLVM Phabricator instance.

[Darwin] Fix OS version checks inside simulators
ClosedPublic

Authored by yln on Jul 16 2020, 12:07 PM.

Details

Summary

compiler-rt checks OS versions by querying the Darwin kernel version.
This is not necessarily correct inside the simulators if the simulator
runtime is not aligned with the host macOS. Let's instead check the
SIMULATOR_RUNTIME_VERSION env var.
rdar://63031937

Diff Detail

Event Timeline

yln created this revision.Jul 16 2020, 12:07 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 16 2020, 12:07 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript
yln added a comment.Jul 16 2020, 12:09 PM

@delcypher
I addressed your remaining comments here: https://reviews.llvm.org/D79979#2156659

delcypher added inline comments.Jul 17 2020, 2:30 PM
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
676

Wait. Why do we go from Simulator OS version -> darwin kernel version -> macOS version? Can't we just compute the macOS version from the simulator OS version using a known offset?

yln marked 2 inline comments as done.Jul 17 2020, 4:31 PM
yln added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
676

Yes, this is correct. The naming scheme change for macOS 11 complicates the mapping (there are two cases now) and this way we can avoid duplicating this logic.

delcypher added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
676

I see. I guess there's a trade-off here:

  • Simulator OS version -> macOS version

Con: We need to duplicate handling of the versioning for the macOS 11 transition.
Pro: The conversion of version numbers is easier understand due to only one conversion rather than two,
Pro: It gives us the ability to preserve the patch (pre macOS 11) or minor (macOS 11 and newer) number.

  • Simulator OS version -> macOS Darwin kernel version -> macOS version

Pro: Allows re-using the logic for the handling of the versioning for the macOS 11 transition.
Con: The conversion of version numbers is harder to understand due to two conversions happening
Con: Can't use the minor/patch version of macOS.. Although maybe we don't need this right now?

I'm not convinced that avoiding duplication of the macOS 11 versioning code is worth the complication it introduces.

However, taking a step back. I think the problem here is that we've decided to use the macOS version numbers as the canonical representation for version checks in the runtime. Given that macOS now has this change in versioning at macOS 11 I don't really think it's the right choice anymore. Some ideas for alternatives:

  1. If we used iOS's versioning scheme as the canonical representation we could potentially make things simpler for ourselves.
  2. We could use a scheme where we instead always work with the native platform version at runtime and instead our branches encode all the versions: e.g.
if (OS_VERSION(10.15, 13.0, 6.0, 13.0) > GetOSVersion()) {
  //.. os specific action.
}

Here OS_VERSION is a macro (or some kind of template magic) that expands to the Version for the platform we are compiling for at compile time. The syntax probably isn't quite right here but the above is meant to just illustrate the idea.

This makes GetOSVersion() really simple because we never need to get kernel versions involved. The implementation would be something like:

if (SANITIZER_IOSSIM) {
  if (auto vers = GetEnv("SIMULATOR_RUNTIME_VERSION")) {
    u16 major, minor;
    ParseVersion(vers, &major, &minor);
    return OSVersion(major, minor, 0);
  }
  // Emit warning here...
}
// Get OS from systl kern.osproductversion
char vers[100];
size_t len = sizeof(vers);
int res = internal_sysctlbyname("kern.osproductversion", vers, &len, nullptr, 0);
CHECK_EQ(res, 0);
return OSVersion(major, minor, 0);

@yln @kubamracek What do you think?

yln marked 2 inline comments as done.Jul 23 2020, 5:15 PM
yln added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
676

I agree that what you outlined above, i.e., something along the lines:

if (OS_VERSION(10.15, 13.0, 6.0, 13.0) > GetOSVersion()) {
  //.. os specific action.
}

would be much better. This is also pretty much what availability checking code supported by language constructs (Swift, objc) looks like.

I would like to keep the scope of this patch small: fix OS version checking in the simulators.
This patch accomplishes that and improves the current state. I am happy to see further improvements as follow-ups (I don't think this patch makes those harder), and therefore would like to continue as is.

I'm not convinced that avoiding duplication of the macOS 11 versioning code is worth the complication it introduces.

I will try to change the patch to go from OS version -> macOS version directly.

yln updated this revision to Diff 280594.Jul 24 2020, 2:49 PM

Map from OS version -> macOS version directly.

yln edited the summary of this revision. (Show Details)Jul 24 2020, 2:49 PM
delcypher requested changes to this revision.Jul 24 2020, 6:57 PM

Looks really good. I just have a few nits.

compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
609

Do we use _ in the name of types?

612

I think we usually use uptr rather than size_t.

620

Suggestion. You could do

uptr env_var_len  = internal_strlen(vers_env);
CHECK_LT(env_var_len, sizeof(vers_str));

as a sanity check.

657

Suggestion: You could add a sanity check here.

CHECK_GE(*major, 10);
if (*major == 10) {
  CHECK_GE(*minor, /*whatever our minimum version is */);
}
676

Cool. You combined my sketch with the patch. This looks great.

This revision now requires changes to proceed.Jul 24 2020, 6:57 PM
delcypher added inline comments.Jul 24 2020, 6:59 PM
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
643

Oh you have an early return (which is great) but if you do the sanity check you have to do it here too.

yln marked 5 inline comments as done.Jul 27 2020, 5:49 PM
yln added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
620

The check CHECK_LT(len, sizeof(vers_str)); below (which applies to both sides of the if-else) should accomplish the same.

strlcpy() returns the total length of the string it tried to create (essentially strlen(src)), which should be smaller than the dst buffer.

687

Switching this over to VersStr as well and adding assert for the length.

yln updated this revision to Diff 281090.Jul 27 2020, 5:50 PM
yln marked an inline comment as done.

vers_str -> VersStr, size_t -> uptr, add additional CHECK assert.

delcypher added inline comments.Jul 27 2020, 6:18 PM
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
657

@yln Did you miss this one? It's only a suggestion so it's fine not to do it but if you're not going to do it then it would be good to say why.

yln marked an inline comment as done.Jul 27 2020, 6:29 PM
yln added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
657

This specific code behaves predictably for any version numbers (even if it's producing invalid numbers for invalid inputs). It's more a constraint of application in compiler-rt that we don't support anything pre 10.9, but the code is okay with anything.

In the previous iteration of this I had a similar-looking CHECK, because the mapping was defined in a way where we had an "invalid case":

darwin_kernel_vers >= 20 -> macOS 11+  //  -20 + 11
darwin_kernel_vers >= 4 -> macos 10.x  // -4
Invalid, smaller versions: -4 would wrap around // guard with CHECK

Hope this makes sense.

delcypher accepted this revision.Jul 27 2020, 6:33 PM

LGTM except for the lint checks failing. Please fix those before landing.

compiler-rt/lib/sanitizer_common/tests/sanitizer_mac_test.cpp
34

Could you fix these lint checks before you land this?

This revision is now accepted and ready to land.Jul 27 2020, 6:33 PM
This revision was landed with ongoing or failed builds.Jul 28 2020, 9:28 AM
This revision was automatically updated to reflect the committed changes.