Fix buffer overflows in SNMP MIB parser token handling#2391
Fix buffer overflows in SNMP MIB parser token handling#2391neosys007 wants to merge 3 commits intosquid-cache:masterfrom
Conversation
The get_token() function in lib/snmplib/parse.c does not receive a buffer size parameter and writes to the caller's buffer without bounds checking. Callers use fixed 64-byte stack buffers (token[], syntax[], nexttoken[]), which can be overflowed by oversized tokens from malicious MIB files. This fix: 1. Adds MAX_TOKEN_SIZE constant (64 bytes) matching caller buffer sizes 2. Adds bounds checking in get_token() before writing to the token buffer 3. Replaces unsafe strcpy/strcat with snprintf in parse_objecttype() The vulnerability could be triggered by a malicious MIB file containing an oversized identifier in a SEQUENCE OF declaration, leading to stack buffer overflow. Signed-off-by: hou pengpeng <pengpeng@iscas.ac.cn>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
|
Fixed line wrapping and removed list duplicating what the patch says. |
lib/snmplib/parse.c
Outdated
| #define QUOTE 37 | ||
|
|
||
| /* Maximum token buffer size - must match caller's buffer size */ | ||
| #define MAX_TOKEN_SIZE 64 |
There was a problem hiding this comment.
Don't just document as used for buffers. This constant needs to actually be used to declare the buffers whose size it controls.
If the snmp_mib_tree::label buffer is one of those, then this constant will need to be placed in include/parse.h.
Also, due to how this is being used the documentation needs to say "including NUL terminator" to help avoid future mistakes.
There was a problem hiding this comment.
Agreed. I will remove MAX_TOKEN_SIZE in v2 rather than trying to reuse it for multiple buffers. The next revision will make get_token() capacity-aware and have callers pass their actual buffer size explicitly.
rousskov
left a comment
There was a problem hiding this comment.
Thank you for working on this bug fix.
lib/snmplib/parse.c
Outdated
| #define QUOTE 37 | ||
|
|
||
| /* Maximum token buffer size - must match caller's buffer size */ | ||
| #define MAX_TOKEN_SIZE 64 |
There was a problem hiding this comment.
Please do not add this constant. It does not prevent future problems like the ones fixed by this PR, it does not expose the dangers in caller contexts, and it does not match the actual buffer sizes in existing code (e.g., getoid() tokens are 128 bytes long, not 64 bytes AFAICT).
Instead, add bufSize or bufCapacity parameter to get_token() and adjust get_token() callers to pass sizeof(token) or similar values, as appropriate. We could do better in C++, but I do not know of a better solution for this C code.
There was a problem hiding this comment.
Agreed.
I will remove the constant in the next revision and change get_token() to take a caller-provided buffer capacity instead.
I will also audit and update all get_token() callers to pass their actual destination size (including the larger getoid() token buffers), rather than assuming a shared fixed size.
lib/snmplib/parse.c
Outdated
| if (cp - token < MAX_TOKEN_SIZE - 1) | ||
| *cp++ = ch; |
There was a problem hiding this comment.
Please do not ignore problems -- this if statement needs an else clause that would immediately return a parsing error instead.
I have not checked carefully, but I suspect the best way to handle this would be to add an ERROR token type with -2 as a value. One could abuse existing CONTINUE for this, but that would be too misleading IMO. tokens[] array may need to be adjusted. print_error() will need to be adjusted. Other adjustments may be needed, but all the callers I have checked should handle the new type correctly without changes; I have not checked all the callers though; please do that.
There was a problem hiding this comment.
Agreed.
I will not keep the current "skip/truncate" behavior in the next revision.
I agree that reusing CONTINUE here would be misleading, so I will use a dedicated error token instead. I will rework this to return an explicit parsing error on token overflow, update the related handling around tokens[] and print_error(), and audit all get_token() callers to make sure the new error path is handled consistently.
lib/snmplib/parse.c
Outdated
| if (cp - token < MAX_TOKEN_SIZE - 1) | ||
| *cp++ = ch; |
There was a problem hiding this comment.
Please do not ignore problems -- this if statement needs an else clause that would immediately return a parsing error instead. See another change request for details.
There was a problem hiding this comment.
Agreed.
I will remove this silent truncation logic in the next revision. On overflow, get_token() will return an explicit parsing error immediately instead of continuing.
lib/snmplib/parse.c
Outdated
| switch (type) { | ||
| case SEQUENCE: | ||
| strcpy(syntax, token); | ||
| snprintf(syntax, sizeof(syntax), "%s", token); |
There was a problem hiding this comment.
If you are adding snprintf() calls, please do not ignore call failures. It may not be trivial, but it is necessary. This concern applies to all snprintf() calls added in this PR.
There was a problem hiding this comment.
Agreed. I will remove these added snprintf() calls in v2 and fix this at the get_token() boundary instead.
lib/snmplib/parse.c
Outdated
| if (len < sizeof(syntax) - 1) { | ||
| snprintf(syntax + len, sizeof(syntax) - len, " %s", nexttoken); |
There was a problem hiding this comment.
Please do not duplicate snprintf() protections manually. Check for snprintf() failures instead of adding another if without an error-handling else clause.
You may explicitly assert that len <= sizeof(syntax) to avoid doubts about unsigned underflows in snpritf() call argument computation. Preceding code ought to make that assertion true.
There was a problem hiding this comment.
Agreed.
I will not keep this manual pre-check pattern in the next revision. I am reworking the fix so that these added snprintf()-based syntax-buffer changes go away, and any remaining snprintf() usage will check return values directly rather than relying on a separate size guard.
|
I added "SNMP" to the PR title to provide more context and replaced "overflow" with "overflows" because this PR fixes several different overflow bugs AFAICT. |
|
Thank you for the detailed review. I agree with the requested direction. I will rework this patch to:
The underlying bug report stays the same, but I agree the fix should be done at the get_token() API level rather than with a shared constant. I will post an updated revision. |
|
Posted v2 in commit 2c3603c. This revision:
I also audited the get_token() callers, including the larger getoid() token buffer. |
2c3603c to
84bf780
Compare
|
I updated the PR to fix a real build failure in the current revision ( The updated head is now The new workflow run appears to be waiting for maintainer approval. Could a maintainer please approve/re-run the PR checks? |
|
@neosys007, just FYI: As you fix CI tests, there is usually no need to squash and/or force-push your changes (and there is some minor harm/inconvenience in those actions). After your code gets approved, Anubis, our merge bot, will squash your commits before merging them into master. If you do not want to wait for us to approve CI builds, you may run these tests in your repository. There are several ways to do that. One of them is to create a pull request in your repository -- it will be just for you to run CI tests, nobody from the Project will pay attention to it... P.S. I will review and adjust your changes ASAP, but there is no need to wait for that to happen if you want to keep fixing CI failures. |
|
Thank you, that is very helpful. Understood. I will avoid further squash/force-push updates and use follow-up commits from here. I am investigating the remaining source-maintenance failure now, and may use a PR in my fork to run CI while narrowing it down. Best regards, |
|
I added a follow-up commit ( The new workflow run appears to be waiting for maintainer approval again. Could a maintainer please approve/re-run the PR checks? |
|
CI tests are all green. The ball is fully on my side now. |
There was a problem hiding this comment.
PR description: This vulnerability could be triggered by a malicious MIB file containing an oversized identifier in a SEQUENCE OF declaration, leading to stack buffer overflow.
I believe the above assertion is false (and this PR changes are untested): AFAICT, no MIB file, no matter how malicious, can trigger this vulnerability because the code in question is unreachable. It is unreachable because it is unused since 1998 commit dba79ac. Squid hard-codes its MIB and, hence, never calls init_mib() which reads MIB and is the only function that (indirectly) calls get_token().
If you agree that get_token() is unreachable and unused, then please close this PR.
Needless to say, we do need to remove unused code from Squid. There is a lot of it!1 We already have an active project that focuses on unused code removal. The corresponding code changes are currently stuck in the backlog, but they do remove get_token() among many other unused functions. PRs that focus on removing some of that code are not welcome at this point because they would create more conflicts for that backlogged project to deal with.
Footnotes
-
Which partially explains why it took me a while to notice that this PR modifies an unused function. I am sorry for not realizing this sooner. This delay is one of the many reasons we need to test pull requests before posting them! ↩
The get_token() function in lib/snmplib/parse.c does not
receive a buffer size parameter and writes to the caller's
buffer without bounds checking. Callers use fixed 64-byte stack
buffers (token[], syntax[], nexttoken[]), which can be
overflowed by oversized tokens from malicious MIB files.
This vulnerability could be triggered by a malicious MIB file
containing an oversized identifier in a SEQUENCE OF declaration,
leading to stack buffer overflow.