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

nanocoap: fix server-side option_count overflow #10754

Merged
merged 3 commits into from
Jan 14, 2019

Conversation

kaspar030
Copy link
Contributor

Contribution description

nanocoap limits the number of options it parses, but didn't check if that limit was reached when parsing a packet. (See report in #10753).

This PR provides a fix. It also adds a unittest that exposes the flaw.

Fixes #10753.

Testing procedure

Run unittests without uppermost commit. The nanocoap test should crash on native. Run again with topmost commit. Tests should succeed.

Issues/PRs references

See #10753.

@kaspar030 kaspar030 added Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) Area: security Area: Security-related libraries and subsystems Area: CoAP Area: Constrained Application Protocol implementations labels Jan 11, 2019
@kaspar030 kaspar030 added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR CI: run tests If set, CI server will run tests on hardware for the labeled PR labels Jan 11, 2019
@kb2ma
Copy link
Member

kb2ma commented Jan 11, 2019

Just took a first look. The fix looks appropriate. Need to do a little more testing.

In the unit test, how do I know that the printed pointer values indicate a good test? Just that they are close to each other? Is it possible to be more specific? It's odd to print values as part of a unit test. I would think it makes more sense to compare the values within the test, and fail the test if the difference is outside of some range.

@kaspar030
Copy link
Contributor Author

In the unit test, how do I know that the printed pointer values indicate a good test? Just that they are close to each other? Is it possible to be more specific? It's odd to print values as part of a unit test. I would think it makes more sense to compare the values within the test, and fail the test if the difference is outside of some range.

When I wrote the test, I wasn't sure that it would (without the fix) crash.
The coap options array is the last field in coap_pkt_t. My logic was that if I have pkt on the stack and directly after it another value (guard_value), if coap_parse() exceeded the options array, it would overwrite the guard value. But without getting an address of both of the variables, the compiler might just keep them in registers. Thus the printf...
While writing this I realized that any overflow would go to stack space before (or above) pkt, not after (or below), on all our platforms. So I think the guard_value declaration must be moved before pkt.

Does this make sense at all or does anyone have a better idea on how to detect an overflow of the options array?

@kb2ma
Copy link
Member

kb2ma commented Jan 11, 2019

Sorry, I have no insights. In keeping with the current approach, you could put a guard value on both sides to be safe.

Another approach is to set the maximum number of options (NANOCOAP_NOPTS_MAX) to a small value and try a functional test. That's what I'm working on.

@kb2ma
Copy link
Member

kb2ma commented Jan 11, 2019

You also could craft an array with NANOCOAP_NOPTS_MAX options, parse it, and expect success. Then add another option to it and expect -ENOMEM. That should be sufficient for this test. No need to get into the low level overflow detection.

Copy link
Member

@kb2ma kb2ma left a comment

Choose a reason for hiding this comment

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

Need to fix option count comparison.

@@ -111,6 +111,9 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
DEBUG("optpos option_nr=%u %u\n", (unsigned)option_nr, (unsigned)optpos->offset);
optpos++;
option_count++;
if (option_count >= NANOCOAP_NOPTS_MAX) {
Copy link
Member

@kb2ma kb2ma Jan 11, 2019

Choose a reason for hiding this comment

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

It should be OK to include NANOCOAP_NOPTS_MAX options. This test should be moved above addition to the array, probably line 109. The test then should be:

if (option_count == NANOCOAP_NOPTS_MAX) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. The new test seems to be working, it exposed that using "==" here caused to write one behind the options array. So I moved the check to the beginning of the if case.

@kaspar030
Copy link
Contributor Author

Then add another option to it and expect -ENOMEM. That should be sufficient for this test. No need to get into the low level overflow detection.

I think I found a way. At least on native it works as expected.

Copy link
Member

@kb2ma kb2ma left a comment

Choose a reason for hiding this comment

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

I agree the verification in coap_parse() is in the right place now. See my suggested alternative unit test in kapar030/#41. We're getting closer...

sys/net/application_layer/nanocoap/nanocoap.c Outdated Show resolved Hide resolved
@@ -433,6 +434,40 @@ static void test_nanocoap__server_reply_simple_con(void)
TEST_ASSERT_EQUAL_INT(COAP_TYPE_ACK, coap_get_type(&pkt));
}

static void test_nanocoap__server_option_count_overflow_check(void)
Copy link
Member

Choose a reason for hiding this comment

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

I kind of understand what you are doing to test the overflow. Why are there 42 options in guard data? How does the unit test work? I think it's important for others to understand this without a lot of thought/investigation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added some comments.

@kaspar030 kaspar030 removed the CI: run tests If set, CI server will run tests on hardware for the labeled PR label Jan 12, 2019
@kaspar030
Copy link
Contributor Author

See my suggested alternative unit test in kapar030/#41.

I have cherry-picked the commit. IMO more test code doesn't hurt, even if they test the same thing.

@kb2ma
Copy link
Member

kb2ma commented Jan 13, 2019

I agree it's OK to include both tests since they verify from different directions. These tests run OK for me on native and samr21-xpro. I verified that allowing coap_parse() to write an extra option makes the tests fail.

Thanks for adding the comments in the test. The only remaining question for me is why are there 42 options in guard_data instead of only one or two. When I allowed the parse to overwrite the guard data, I found the overwrite occurred in the first few bytes.

@kb2ma kb2ma added Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 3-testing The PR was tested according to the maintainer guidelines labels Jan 13, 2019
@kaspar030
Copy link
Contributor Author

The only remaining question for me is why are there 42 options in guard_data instead of only one or two. When I allowed the parse to overwrite the guard data, I found the overwrite occurred in the first few bytes.

@nmeum provided this packet in his original report, and I was lazy enough to take it as-is. The number just needs to be higher than NANOCOAP_NOPT_MAX. (42 seemed to be the right answer after thinking for 7.5 million years.) I've added a note stating that 42 is basically randomly chosen.

@kb2ma
Copy link
Member

kb2ma commented Jan 14, 2019

OK, good enough. ACK, please squash.

@kaspar030
Copy link
Contributor Author

Thanks @kb2ma!

@kaspar030 kaspar030 merged commit 9767dd3 into RIOT-OS:master Jan 14, 2019
@jia200x
Copy link
Member

jia200x commented Jan 14, 2019

@kaspar030 could you provide a backport to the 2018.10 branch?

@kaspar030 kaspar030 deleted the fix_nanocoap_optlen branch January 22, 2019 10:22
@aabadie aabadie added this to the Release 2019.01 milestone Jan 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: CoAP Area: Constrained Application Protocol implementations Area: security Area: Security-related libraries and subsystems CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 3-testing The PR was tested according to the maintainer guidelines Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

nanocoap: options buffer overflow
4 participants