-
Notifications
You must be signed in to change notification settings - Fork 155
Fix multiple heap buffer overflow vulnerabilities in URL parsing #1045
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
Conversation
| assert(_iovcnt); | ||
| assert(n <= _v.iov_len); | ||
| if (n < _v.iov_len) { _v += n; } | ||
| else { _v = *++_iov; _iovcnt--; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an internal helper class inside a .cpp file. Its usage ensures the validness of *++_iov. If you are worried about this, add an assert instead.
net/http/url.cpp
Outdated
| m_target = rstring_view16(pos, url.size()); | ||
| pos += p+1; | ||
| m_query = rstring_view16(pos, url.size() - (p + 1)); | ||
| auto query_len = (p + 1 <= url.size()) ? (url.size() - (p + 1)) : 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use photon::sat_sub() to avoid overflow.
net/http/url.h
Outdated
| class URL { | ||
| protected: | ||
| const char* m_url = nullptr; | ||
| std::string_view m_url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why string_view is necessary for m_url?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code, you're absolutely right. The std::string_view m_url is NOT necessary. The original const char* m_url was fine because:
- rstring_view::operator|(const char*) calls to_abs(s) which simply does s + _offset with
the stored _length - no strlen() is involved
- All the rstring_view16 members (m_host, m_path, etc.) already store both offset and length
- The actual heap-buffer-overflow was likely caused by something else, not by the type of m_url
| namespace net { | ||
| namespace http { | ||
|
|
||
| void URL::fix_target() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually a private helper function only used in URL::from_string(). I would suggest move the function body into URL::from_string().
ee3c422 to
78b2565
Compare
|
Hi , I've addressed all your feedback: |
net/http/url.h
Outdated
|
|
||
| std::string to_string() { | ||
| return m_url; | ||
| return std::string(m_url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simply return m_url; is fine.
net/http/url.cpp
Outdated
| if (port_len > 0) { | ||
| uint64_t port_val = 0; | ||
| estring_view port_str(url.data(), port_len); | ||
| if (port_str.to_uint64_check(&port_val)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check for port_val > 65535
net/http/url.cpp
Outdated
| return; | ||
|
|
||
| // Fix target if needed (was previously a separate fix_target() function) | ||
| estring_view t = m_url | m_target; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can still use DEFER({ ... }); to execute this code snippet on all returns. If that's ugly, define a lambda, and invoke it with DEFER.
net/http/url.cpp
Outdated
| #include "url.h" | ||
| #include <photon/common/alog.h> | ||
| #include <photon/common/alog-stdstring.h> | ||
| #include <photon/common/utility.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's necessary. The file now uses:
- DEFER macro (line 35) - defined in utility.h
- sat_sub function (lines 102, 117) - also defined in utility.h
Both are required for the code to compile.
common/estring.h
Outdated
| // Ensure offset and length are within bounds of the string_view | ||
| if (s.length() < _offset + _length) { | ||
| // Return empty view if out of bounds | ||
| return estring_view("", (size_t)0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simply return {};
net/http/url.cpp
Outdated
| if (need_optional_port(*this)) m_host_port = rstring_view16(m_host.offset(), m_host.length() + port_len); | ||
| } else if (url.front() == ':') { | ||
| url.remove_prefix(1); // Skip ':' | ||
| // Parse port number, find the end of digit sequence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic of finding the digits are already included in to_uint64_check(). You may not need to do it yourself?
net/http/url.cpp
Outdated
| m_query = rstring_view16(0, 0); | ||
| m_target = m_path; | ||
| } else { | ||
| p = url.find_first_of("?"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do NOT repeat similar logics, such as find_first_of("?") and its related processing.
net/http/url.cpp
Outdated
| if (port_str.to_uint64_check(&port_val) && port_val <= 65535) { | ||
| m_port = static_cast<uint16_t>(port_val); | ||
| } else { | ||
| m_port = m_secure ? 443 : 80; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider moving the assignment m_port = m_secure ? 443 : 80; upward, immediately after m_secure is assigned, in order to avoid code duplication.
net/http/url.cpp
Outdated
| url.remove_prefix(port_len); | ||
|
|
||
| if (url.empty()) { | ||
| m_path = rstring_view16(pos, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
goto the then part of prior if (url.empty()) { ... }, in order to avoid code duplication.
net/http/url.cpp
Outdated
| m_query = rstring_view16(0, 0); | ||
| m_target = m_path; | ||
| return; | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original form of linear code structure (with multiple early returns) is more preferable than nesting elses and {...}s, as it is easier to read and understand.
If there were vulnerabilities, please try to fix them without changing code structure, unless it's not proper.
net/http/url.cpp
Outdated
| url.remove_prefix(1); // Skip ':' | ||
| pos += 1; // +1 for ':' | ||
| // Find end of port digits | ||
| auto port_end = url.find_first_not_of("0123456789"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may not need to find the first non-digit, because estring_view::to_uint64_check() already does that. It stops at the first non-digit.
So, just call it directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out that to_uint64_check doesn't return the number of valid digits, so it's not possible to avoid find_first_not_of() at present.
Leave it as is for now. I'll address this issue in another PR.
|
Let me address the issue directly and push to your branch. |
|
Oh, I'm sorry to have mistakenly force pushed my I have pushed your commits to https://github.com/lihuiba/PhotonLibOS/tree/ShadowRider and opened a temporary PR at #1057 You can pull the commits (with my update) back, push them to your repo again and reopen the PR, or let me merge the new PR. |
|
@ShadowRider09 As you were not responding, we merged the PR #1057 in order to continue improving related code. Thanks again for your contribution! |
|
#1058 is the follow on PR |
fixed_crash.zip