Skip to content

admin: Fix crashes on invalid FieldMasks#16979

Merged
zuercher merged 6 commits intoenvoyproxy:mainfrom
paul-r-gall:FixConfigDumpCrash
Jun 23, 2021
Merged

admin: Fix crashes on invalid FieldMasks#16979
zuercher merged 6 commits intoenvoyproxy:mainfrom
paul-r-gall:FixConfigDumpCrash

Conversation

@paul-r-gall
Copy link
Copy Markdown
Contributor

Signed-off-by: Paul Gallagher pgal@google.com

Note: Though this is an unexpected crash, this change does not require the security process, as the admin interface already has documented methods of triggering a crash (/quitquitquit), and should always be protected anyway.

Commit Message: admin: Fix crashes on invalid FieldMasks
Additional Description: When a field mask is invalid, TrimMessage will LOG(FATAL). Since explicitly checking validity of a field mask via FieldMaskUtil::IsValidFieldMask for a message requires compile-time knowledge of the message type, the best we can do is catch the exception thrown by TrimMessage and return an error.
Risk Level: Low
Testing: Unit tests.
Docs Changes: None, this aligns actual behavior with documented behavior.

Signed-off-by: Paul Gallagher <pgal@google.com>
@jmarantz
Copy link
Copy Markdown
Contributor

Heh, /quitquitquit should exit cleanly and not "crash" :)

Anyway I'll take a look at this.

@jmarantz jmarantz self-assigned this Jun 15, 2021
Copy link
Copy Markdown
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

At a high level this looks good, with a couple of small issues and one question.

Would it make sense to do the exception trapping in config_dump_Handler.cc -- just wrap the try around addAllConfigDump? I think that might be consistent with the way we filter bad regexes in the /stats handler, but I'll leave it to you to check :)

// trimResourceMessage will LOG(FATAL) if `field_mask` is malformed.
TRY_ASSERT_MAIN_THREAD { trimResourceMessage(field_mask, msg); }
END_TRY
catch (...) {
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.

can you just catch a specific exception here? Looking at source/common/protobuf/utility.cc; I think you can use EnvoyException, which is the usual pattern.

Then you can also give the text from the exception (e.what() I think).

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.

Actually, it seems as though this isn't an EnvoyException but instead a std::exception, and calling e.what() just returns "std::exception", which isn't particularly helpful.

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.

Can you debug who is throwing this std::exception?

In theory no one in Envoy should be throwing an exception that is not derived from EnvoyException. However, if this comes from the STL then that won't be true.

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.

This is coming from a LOG(FATAL) here.

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.

Can we see what's going on up stack and make it not call LOG(FATAL)?

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.

Can you use this utility inside trimResourceMessage after the unpack?

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 meant protobuf. Thanks.

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.

Unfortunately I don't think so; all messages are dynamically constructed from DynamicMessageFactory inside trimResourceMessage, and IsValidFieldMask is templated and requires compile-time knowledge of the proto type, e.g. IsValidFieldMask<Cluster>(mask).

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.

Is there anything we can do to pre-validate the field_mask to avoid crashing in protobuf? Catching the throw that emanates from a LOG(FATAL) seems non-robust.

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 added a method which essentially does the same thing as IsValidFieldMask but doesn't require compile-time knowledge of types. LMK if this works for you!

ProtobufUtil::FieldMaskUtil::TrimMessage(field_mask, message.get());
}
END_TRY
catch (...) {
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.

catch (EnvoyException e)

header_map, response));
EXPECT_EQ("FieldMask paths: \"cluster.transport_socket_matches.name\"\n could not be "
"successfully used.",
response.toString());
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.

can you also make sure that headerMap.ContentTtype == "text/plain" here? If that was somehow 'text/html' then the error message could contain an XSS.

I think there's a ContentTypeValues.XXX value for that.

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.

Done.

getCallback("/config_dump?mask=bootstrap.node.extensions.name", header_map, response));
EXPECT_EQ("FieldMask paths: \"bootstrap.node.extensions.name\"\n could not be "
"successfully used.",
response.toString());
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.

ditto: check for content-type: text/plain

Copy link
Copy Markdown
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

/wait

Signed-off-by: Paul Gallagher <pgal@google.com>
Signed-off-by: Paul Gallagher <pgal@google.com>
addAllConfigToDump(dump, mask, include_eds);
auto err = addAllConfigToDump(dump, mask, include_eds);
if (err.has_value()) {
response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Text);
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.

sorry can you also add

   response_headers.addReference(
       Http::Headers::get().XContentTypeOptions,
       Http::Headers::get().XContentTypeOptionValues.Nosniff);

Just a little paranoia to make sure browsers don't flip into HTML mode if some text looks like HTML. Just a bit of paranoia based on past browser behavior :)

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.

Done.

Signed-off-by: Paul Gallagher <pgal@google.com>
jmarantz
jmarantz previously approved these changes Jun 21, 2021
Copy link
Copy Markdown
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

basically LGTM with a few minor nits you can take care of, but I'll turn it over to senior maintainers. Thanks for doing this!

/assign-from @envoyproxy/senior-maintainers

@repokitteh-read-only
Copy link
Copy Markdown

@envoyproxy/senior-maintainers assignee is @zuercher

🐱

Caused by: a #16979 (review) was submitted by @jmarantz.

see: more, trace.

Signed-off-by: Paul Gallagher <pgal@google.com>
Copy link
Copy Markdown
Member

@zuercher zuercher left a comment

Choose a reason for hiding this comment

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

I think this looks good, modulo one nit and the clang-tidy errors. Those are

  • CheckFieldMaskAndTrimMessage vs checkFieldMaskAndTrimMessage
  • requiring braces around all control statements bodies (the if at line config_dump_handler.cc:110)

// below.
//
//
//
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.

nit: rm extra blank comment lines

Signed-off-by: Paul Gallagher <pgal@google.com>
Copy link
Copy Markdown
Member

@zuercher zuercher left a comment

Choose a reason for hiding this comment

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

Thanks!

@zuercher zuercher merged commit fd39c79 into envoyproxy:main Jun 23, 2021
@paul-r-gall paul-r-gall deleted the FixConfigDumpCrash branch June 23, 2021 19:38
chrisxrepo pushed a commit to chrisxrepo/envoy that referenced this pull request Jul 8, 2021
When a field mask is invalid, TrimMessage will LOG(FATAL). Since explicitly checking validity of a field mask via FieldMaskUtil::IsValidFieldMask for a message requires compile-time knowledge of the message type, the best we can do is catch the exception thrown by TrimMessage and return an error.

Risk Level: Low
Testing: Unit tests.
Docs Changes: None, this aligns actual behavior with documented behavior.
Release Notes: n/a

Signed-off-by: Paul Gallagher <pgal@google.com>
Signed-off-by: chris.xin <xinchuantao@qq.com>
leyao-daily pushed a commit to leyao-daily/envoy that referenced this pull request Sep 30, 2021
When a field mask is invalid, TrimMessage will LOG(FATAL). Since explicitly checking validity of a field mask via FieldMaskUtil::IsValidFieldMask for a message requires compile-time knowledge of the message type, the best we can do is catch the exception thrown by TrimMessage and return an error.

Risk Level: Low
Testing: Unit tests.
Docs Changes: None, this aligns actual behavior with documented behavior.
Release Notes: n/a

Signed-off-by: Paul Gallagher <pgal@google.com>
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.

4 participants