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 memory safety issues #260

Merged
merged 10 commits into from
Jan 6, 2022
Merged

Fix memory safety issues #260

merged 10 commits into from
Jan 6, 2022

Conversation

alpire
Copy link
Contributor

@alpire alpire commented Jan 5, 2022

This PR fixes some memory safety issues reported by OSS-Fuzz in ucl_add_string_fuzzer that were labeled as security relevant:

The PR also fixes a couple issues I ran into while fuzzing locally, mainly in variable expansion to avoid out-of-bounds reads and writes. Each fix is in its own commit describing the issue and the fix. I'd be happy to split the PR if that made it easier to review (or to scrap the commits if you think they are not valuable)

The fuzzer still reports out-of-bounds read when parsing values, as the strto* functions assume the input buffer is null terminated. The ucl_add_string_fuzzer does not give a null-terminated buffer, but I wasn't sure if that was a proper use of the API.

I ran make check and got the same failures as the main branch. If there's other way to tests my changes, I'd be happy to do that as well if you point me in the right direction.

If the string ends with a '\', the function tried to read the next
character before checking bounds. This commit move the bounds check
before the read to avoid the out-of-bounds read.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21578
If the string ends with a Multiline terminator without a newline, the
function tried to read the next character to check for a newline without
checking if the pointer was past the end of the buffer. This commit adds
a bounds check and return early with an error in case of missing
newline.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21579
If the input contains '${' but no following '}', ucl_check_variable
should still increment out_len since ucl_expand_variable will copy the
'$' in the destination buffer.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24591
The strncmp call could read past the bounds of the haystack. The loop
now stop when the remaining data in the haystack cannot contain the
needle.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=28135
If the input ends in '$', calling ucl_check_variable will result in an
out-of-bounds read.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34755
Keys may have null bytes, when they are decoded from json in
ucl_unescape_json_string and contain \u0000. Not copying the full key
resulted in out-of-bounds reads. The copy now relies on memcpy and
keylen instead of strdup.

Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=38579
Fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=38675
This macro is often used in loops before checking whether the end of
chunk condition. Adding a bounds check in there prevents reading past
the buffer.
This commit improves variable expansion and adds:
- an in_len argument to the function in order to avoid reading past the
  end of the src buffer
- a check that the variable name is followed by '}' in strict mode
- extra bounds check to prevent out of bounds reads and writes
Copy link
Owner

@vstakhov vstakhov left a comment

Choose a reason for hiding this comment

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

Thank you very much for the proposed changes! The only place that might break the compatibility is key copy that will be no longer zero terminated. I would suggest to add a trailing \0 for safety and sanity (well, as long we can consider zero terminated strings sane).

* @return
*/
static const char *
ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
size_t remain, unsigned char **dest)
size_t in_len, unsigned char **dest, size_t out_len)
Copy link
Owner

Choose a reason for hiding this comment

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

Yes, this name is more fortunate, I agree.

@@ -517,7 +528,7 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,

p = src;
while (p != end) {
if (*p == '$') {
if (*p == '$' && p + 1 != end) {
Copy link
Owner

Choose a reason for hiding this comment

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

Good catch!

@@ -1919,7 +1919,7 @@ ucl_inherit_handler (const unsigned char *data, size_t len,

/* Some sanity checks */
if (parent == NULL || ucl_object_type (parent) != UCL_OBJECT) {
ucl_create_err (&parser->err, "Unable to find inherited object %*.s",
ucl_create_err (&parser->err, "Unable to find inherited object %.*s",
Copy link
Owner

Choose a reason for hiding this comment

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

Oh, I always forget about the order of * and .. In Rspamd, I have the own printf implementation which has distinction between %.*s and %*.s. But here we use a generic libc printf...

if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
new->trash_stack[UCL_TRASH_KEY] = malloc(other->keylen);
memcpy(new->trash_stack[UCL_TRASH_KEY], other->trash_stack[UCL_TRASH_KEY], other->keylen);
Copy link
Owner

Choose a reason for hiding this comment

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

This change is really good, however, I think it will still be a good idea to terminate key with \0. Otherwise, it will definitely break functions like ucl_object_key which just return a const char*, e.g. how it is done in ucl_copy_value_trash function.

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 makes a lot of sense. I added the null-byte terminator in 110428d.

@alpire alpire requested a review from vstakhov January 5, 2022 15:34
@vstakhov
Copy link
Owner

vstakhov commented Jan 6, 2022

Thank you again, much appreciated!

@vstakhov vstakhov merged commit 64557c0 into vstakhov:master Jan 6, 2022
vstakhov added a commit to rspamd/rspamd that referenced this pull request Jan 6, 2022
@vstakhov
Copy link
Owner

vstakhov commented Jan 6, 2022

BTW, I have found an issue with variables expansion after this change, I will fix it shortly.

vstakhov added a commit that referenced this pull request Jan 6, 2022
@alpire
Copy link
Contributor Author

alpire commented Jan 7, 2022

BTW, I have found an issue with variables expansion after this change, I will fix it shortly.

Oops, thanks for fixing it! I'd be happy to add a test for it if you'd like.

c-rosenberg pushed a commit to HeinleinSupport/rspamd that referenced this pull request Feb 27, 2022
c-rosenberg pushed a commit to HeinleinSupport/rspamd that referenced this pull request Sep 21, 2022
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.

None yet

2 participants