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

[postgres_proxy] assert failure with untrusted buffer when onData() #12340

Closed
jianwen612 opened this issue Jul 28, 2020 · 14 comments · Fixed by #16575
Closed

[postgres_proxy] assert failure with untrusted buffer when onData() #12340

jianwen612 opened this issue Jul 28, 2020 · 14 comments · Fixed by #16575
Assignees
Labels
area/postgres help wanted Needs help! investigate Potential bug that needs verification

Comments

@jianwen612
Copy link
Contributor

I'm currently working on fuzz test(which generates random bytes for onData() and onWrite() to see whether we could crash the Envoy) for network-level filters.
When I was testing on postres_proxy filter with some untrusted data, an assert failure occurred inside linearize:
RELEASE_ASSERT(size <= length(), "Linearize size exceeds buffer size");
https://github.com/envoyproxy/envoy/blob/master/source/extensions/filters/network/postgres_proxy/postgres_decoder.cc#L201

This error only happens in fuzzer or when upstream server is on bad state, so it is not security-critical now.
But I think that we could deal with this error more gracefully. (So that we could make the filter more robust to upstream errors, and enable the fuzzer to continue testing it).

My idea is that we could make it just like other invalid error handles in this file, which is return false;, before calling linearize?
This solution looks like this(from line 200):

auto bytesToRead = length - 4;
if(bytesToRead>data.length()){
  return false;
}
message.assign(std::string(static_cast<char*>(data.linearize(bytesToRead)), bytesToRead));

This issue can be reproduced in unit test by adding a case as below(test/extensions/filters/network/postgres_proxy/postgres_decoder_test.cc):

TEST_P(PostgresProxyFrontendEncrDecoderTest, AssertFailure) {
  std::string str_data;
  for(int i=0;i<8;i++){
    str_data.push_back('\0');
  }
  Buffer::OwnedImpl data(str_data);
  decoder_->onData(data, false);
}

If anyone has a better idea, please share with me or make a pull request and link it here. Thanks!
/cc @dio
/cc @fabriziomello
/cc @cpakulski

@fabriziomello
Copy link
Contributor

fabriziomello commented Jul 28, 2020

Hi @jianwen612, thanks for testing it... I totally agree with return false approach to handle invalid states.

And just 5 bytes is enough to throw exception:

TEST_P(PostgresProxyFrontendEncrDecoderTest, AssertFailure) {
  std::string str_data;
  for(int i=0;i<5;i++){
    str_data.push_back('\0');
  }
  Buffer::OwnedImpl data(str_data);
  decoder_->onData(data, false);
}

LGTM

@cpakulski
Copy link
Contributor

I agree that adding return false will help with this one particular test case, but unfortunately is not enough from filter's logic point of view. The reason is that messages pass through the filter one after another. The previous message must be processed successfully in order to start processing the next one. If there is something wrong with one message, for example length indicated in a message header does not reflect the real message's length, then the whole processing goes out of sync and the filter will not be able to find where one message ends and the next one begins. Keeping processing messages will lead to OOM and crash. The current implementation of the filter does not really have a state. It should accept only certain messages in certain states and if it goes out of sync, it should stop interpreting messages or maybe even reset the connection.

@jianwen612 Thanks for testing it. Making this filter to pass fuzz testing requires more changes. I will address this issue in few weeks, as I will be away.

@jianwen612
Copy link
Contributor Author

Hi @jianwen612, thanks for testing it... I totally agree with return false approach to handle invalid states.

And just 5 bytes is enough to throw exception:

TEST_P(PostgresProxyFrontendEncrDecoderTest, AssertFailure) {
  std::string str_data;
  for(int i=0;i<5;i++){
    str_data.push_back('\0');
  }
  Buffer::OwnedImpl data(str_data);
  decoder_->onData(data, false);
}

LGTM

Thanks for pointing this out!

@jianwen612
Copy link
Contributor Author

I agree that adding return false will help with this one particular test case, but unfortunately is not enough from filter's logic point of view. The reason is that messages pass through the filter one after another. The previous message must be processed successfully in order to start processing the next one. If there is something wrong with one message, for example length indicated in a message header does not reflect the real message's length, then the whole processing goes out of sync and the filter will not be able to find where one message ends and the next one begins. Keeping processing messages will lead to OOM and crash. The current implementation of the filter does not really have a state. It should accept only certain messages in certain states and if it goes out of sync, it should stop interpreting messages or maybe even reset the connection.

@jianwen612 Thanks for testing it. Making this filter to pass fuzz testing requires more changes. I will address this issue in few weeks, as I will be away.

Thank you for sharing this! Okey, then I will temporarily disable the fuzzing for this filter.

@alyssawilk alyssawilk added the investigate Potential bug that needs verification label Jul 29, 2020
@cpakulski
Copy link
Contributor

/assign @cpakulski

@stale
Copy link

stale bot commented Aug 29, 2020

This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or other activity occurs. Thank you for your contributions.

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Aug 29, 2020
@cpakulski
Copy link
Contributor

WIP.

@stale stale bot removed the stale stalebot believes this issue/PR has not been touched recently label Sep 2, 2020
@stale
Copy link

stale bot commented Oct 4, 2020

This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or other activity occurs. Thank you for your contributions.

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Oct 4, 2020
@cpakulski
Copy link
Contributor

WIP.

@stale stale bot removed the stale stalebot believes this issue/PR has not been touched recently label Oct 7, 2020
@github-actions
Copy link

github-actions bot commented Dec 9, 2020

This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or "no stalebot" or other activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale stalebot believes this issue/PR has not been touched recently label Dec 9, 2020
@cpakulski cpakulski added no stalebot Disables stalebot from closing an issue area/postgres and removed stale stalebot believes this issue/PR has not been touched recently labels Dec 9, 2020
@mattklein123 mattklein123 added help wanted Needs help! and removed no stalebot Disables stalebot from closing an issue labels Mar 8, 2021
@rostams-lyft
Copy link

Is this still a work-in-progress?

@cpakulski
Copy link
Contributor

@rostams-lyft Yes, I am still working on it.

fabriziomello added a commit to fabriziomello/envoy-postgres-regression that referenced this issue May 18, 2021
Switch to cpakuslki/envoy repo to check issue envoyproxy/envoy#12340
@cpakulski
Copy link
Contributor

@jianwen612 I opened a PR #16575 to fix this issue. Can you help me verify that fuzz tests do not crash the postgres filter? Thanks!

@jianwen612
Copy link
Contributor Author

@jianwen612 I opened a PR #16575 to fix this issue. Can you help me verify that fuzz tests do not crash the postgres filter? Thanks!

Sorry... I don't have the environment to test it now. I've moved to another team.

lizan pushed a commit that referenced this issue Jun 18, 2021
Commit Message:
Validate postgres messages before parsing.

Additional Description:
Introduced InSync and OutOfSync states in decoder. When decoder detects a wrongly formatted message, it stops parsing and moves to OutOfSync state. Continuing parsing after encountering message with wrong syntax may lead to interpreting random bytes as length of the message and possibly causing OOM.

Risk Level: Low
Testing: Added unit tests and run full regression tests.
Docs Changes: No.
Release Notes: No.
Platform Specific Features:
Fixes #12340 

Signed-off-by: Christoph Pakulski <[email protected]>
leyao-daily pushed a commit to leyao-daily/envoy that referenced this issue Sep 30, 2021
Commit Message:
Validate postgres messages before parsing.

Additional Description:
Introduced InSync and OutOfSync states in decoder. When decoder detects a wrongly formatted message, it stops parsing and moves to OutOfSync state. Continuing parsing after encountering message with wrong syntax may lead to interpreting random bytes as length of the message and possibly causing OOM.

Risk Level: Low
Testing: Added unit tests and run full regression tests.
Docs Changes: No.
Release Notes: No.
Platform Specific Features:
Fixes envoyproxy#12340 

Signed-off-by: Christoph Pakulski <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/postgres help wanted Needs help! investigate Potential bug that needs verification
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants