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

Fix crash while parsing malformed manifest files. #1211

Closed
wants to merge 1 commit into from

Conversation

autoantwort
Copy link
Contributor

@@ -1105,6 +1105,7 @@ namespace vcpkg
if (!maybe_manifest_scf)
{
print_error_message(maybe_manifest_scf.error());
msg::println();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes
image

Copy link
Member

Choose a reason for hiding this comment

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

FYI I repeated this in #1219 but #1203 resolves it by making print_error_message itself add the extra newline since I found other occurrences.

BillyONeal added a commit to BillyONeal/vcpkg-tool that referenced this pull request Sep 28, 2023
Alternative to microsoft#1211

Fixes microsoft/vcpkg#33973

I'm not entirely happy with this because it emits extra 'mismatched type' warnings like $.dependencies[0].features[0]: mismatched type: expected a feature in a dependency .
@BillyONeal BillyONeal added the requires:discussion This PR requires discussion of the correct way forward label Sep 28, 2023
@BillyONeal
Copy link
Member

BillyONeal commented Sep 28, 2023

I did some experimentation in BillyONeal@68122ce

  • This needs regression tests, which I added there I am blind and see unit tests here now sorry
  • I don't think making a sentinel value <error> is OK. Either DependencyRequestedFeature enforces the constraint that name is valid, or it doesn't. <error> isn't a valid name either. Either DependencyRequestedFeature should not be enforcing this and the check_exit should just be deleted, or the deserialization infrastructure needs to avoid showing it bad names in the first place. I'm not sure which; I'm going to try to ask @ras0219-msft et al. what the proper fix is.
  • I think we should be emitting a more specific error to help the user when one of the special values is put there.

I'm going to try to ask around tomorrow...

@BillyONeal
Copy link
Member

Seeing as IdentifierDeserializer doesn't return nullopt when the input isn't a valid identifier I suspect the right fix is just deleting the check_exit...

@autoantwort
Copy link
Contributor Author

I suspect the right fix is just deleting the check_exit...

It was there to prevent using "core" and "default" as feature names because depending of the internal representation that are valid feature names. So this check_exit gives you fail fast errors and enforces invariant so that you don't get non obvious errors.

Seeing as IdentifierDeserializer doesn't return nullopt

I changed that to return an nullopt, but then the error message is:

error: while loading /Users/leanderSchulten/git_projekte/vcpkg/vcpkg.json:
$.dependencies[0].features[0].name (an identifier): "core" is not a valid identifier. Identifiers must be lowercase alphanumeric+hypens and not reserved (see https://learn.microsoft.com/vcpkg/users/manifests for more information)
$.dependencies[0].features[0]: mismatched type: expected a feature in a dependency

@BillyONeal
Copy link
Member

It was there to prevent using "core" and "default" as feature names because depending of the internal representation that are valid feature names. So this check_exit gives you fail fast errors and enforces invariant so that you don't get non obvious errors.

Yeah I understand what the intent was, I'm just saying that we have competing intents here, and the rest of the JSON deserializer infrastructure appears to prohibit the deserialized types having semantic constraints like this.

Comment on lines +513 to 522
static std::string map_illegal_names(std::string&& name)
{
return Json::IdentifierDeserializer::is_ident(name) ? std::move(name) : "<error>";
}

Optional<DependencyRequestedFeature> visit_string(Json::Reader& r, StringView sv) const override
{
return Json::IdentifierDeserializer::instance.visit_string(r, sv).map(
[](std::string&& name) { return std::move(name); });
[](std::string&& name) { return map_illegal_names(std::move(name)); });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This should work exactly like Dependency below:

Suggested change
static std::string map_illegal_names(std::string&& name)
{
return Json::IdentifierDeserializer::is_ident(name) ? std::move(name) : "<error>";
}
Optional<DependencyRequestedFeature> visit_string(Json::Reader& r, StringView sv) const override
{
return Json::IdentifierDeserializer::instance.visit_string(r, sv).map(
[](std::string&& name) { return std::move(name); });
[](std::string&& name) { return map_illegal_names(std::move(name)); });
}
Optional<DependencyRequestedFeature> visit_string(Json::Reader& r, StringView sv) const override
{
return Json::IdentifierDeserializer::instance.visit_string(r, sv).map(
[&r](std::string&& name) {
if (!Json::IdentifierDeserializer::is_ident(name))
r.add_generic_error(type_name(), msg::format(msgInvalidDependency));
return std::move(name);
});
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code would still result in a crash since the constructor of DependencyRequestedFeature check_exit that the feature name is not "core" or "default" because there is a member default-features that must be used.

Copy link
Member

Choose a reason for hiding this comment

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

This code would still result in a crash since the constructor of DependencyRequestedFeature check_exit that the feature name is not "core" or "default" because there is a member default-features that must be used.

The point is that things that are deserialized can't have check_exits like that.

Notably, the assert doesn't even succeed in maintaining the invariant that it won't be empty, since:

DependencyRequestedFeature f{"nonempty"};
auto moved = std::move(f);
assert(f.name.empty()); // passes on most standard library implementations

@BillyONeal
Copy link
Member

Please let me know what you think of #1219

@BillyONeal
Copy link
Member

Closed in favor of #1219 on which I marked you as a co-author for discovering the 'bones' of the fix. Thanks again!

@BillyONeal BillyONeal closed this Oct 2, 2023
@autoantwort autoantwort deleted the feature/fix-crash-3 branch October 30, 2023 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
requires:discussion This PR requires discussion of the correct way forward
Projects
None yet
3 participants