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

Invalid NSEC/3 bitmap on non-zero buffer #1338

Merged
merged 4 commits into from Apr 1, 2022

Conversation

rs
Copy link
Contributor

@rs rs commented Feb 6, 2022

If the PackBuffer is used to encode an NSEC/3 record, the bitmap is
xored with the content of the buffer instead of being zeroed first.

The algorithm has been changed so it is able zero bytes without
losing too much performance (around 2x slower).

If the PackBuffer is used to encode an NSEC/3 record, the bitmap is
xored with the content of the buffer instead of being zeroed first.

The algorithm has been changed so it is able zero bytes without
losing too much performance (around 2x slower).
@rs rs requested review from miekg and tmthrgd as code owners February 6, 2022 04:15
@tmthrgd
Copy link
Collaborator

tmthrgd commented Feb 7, 2022

What’s the actual issue here? Is it observable using the packages public API?

@rs
Copy link
Contributor Author

rs commented Feb 7, 2022

I creates invalid DNS messages when PackBuffer is used with a non-zeroed buffer. The existing tests would actually not pass if the generated content was tested (like it is now added).

This is randomly breaking DNSSEC when using PackBuffer.

@miekg
Copy link
Owner

miekg commented Feb 7, 2022 via email

@rs
Copy link
Contributor Author

rs commented Feb 7, 2022

When you reuse buffers, you are not expected to clear them, that would defeat the purpose of reusing buffers. Your own existing tests was expecting a non zero buffer and was failing to generate a valid message.

The performance difference is 10ns, it’s not a big cost. Zeroing the full buffer would cost more.

@miekg
Copy link
Owner

miekg commented Feb 7, 2022

ok,

I don't like the entire re-write of the loop, I could understnad the first iteration. Please see if there is a more minimal change, also use the go std naming scheme, so no underscores like _off

@rs
Copy link
Contributor Author

rs commented Feb 7, 2022

First loop is for window blocks of high order 8 bits and the second is to encode the low order 8 bits bitmaps and the third is to find all additional types fitting in the same bitmap. There is another loop to make sure we zero empty bitmaps before the current one if any. I don't see a more efficient way to do it with less loops. Previous implementation couldn't know which bytes to 0.

I'll rename _off.

@rs
Copy link
Contributor Author

rs commented Feb 7, 2022

Please have a look at the last commit. I renamed a few vars + added some comments for clarity.

@rs
Copy link
Contributor Author

rs commented Feb 9, 2022

@miekg are you considering merging this fix?

@tmthrgd
Copy link
Collaborator

tmthrgd commented Feb 10, 2022

Considering we only advance off in one place, why can't we just put a zeroing loop there to zero that windows data? Plus one to zero the first window and we're done. I don't think we need to rewrite or overcomplicate this.

@rs
Copy link
Contributor Author

rs commented Feb 10, 2022

You don't know the number of lower order 8bit bitmap blocks in advance, and the previous algorithm had no way to know if the current block its writing in needs to be zeroed. The previous algorithm also not give you a hint on the number of blocks was skipped and thus need to be zeroed as well.

@tmthrgd
Copy link
Collaborator

tmthrgd commented Feb 10, 2022

But we can trivially know the maximum length for a given window and just opportunistically zero up to that.

@rs
Copy link
Contributor Author

rs commented Feb 11, 2022

Sure you can, it will be another loop and performance will be probably the same or worth.

@rs
Copy link
Contributor Author

rs commented Feb 11, 2022

I reverted to the previous algo with window size compute+0 on each window boundaries.

@rs
Copy link
Contributor Author

rs commented Mar 5, 2022

@miekg are you still considering merging this fix?

Copy link
Collaborator

@tmthrgd tmthrgd left a comment

Choose a reason for hiding this comment

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

This is a lot of complexity that I'm not the biggest fan off. What's the performance like of just zeroing the rest of msg?

@rs
Copy link
Contributor Author

rs commented Mar 12, 2022

It depends on the size of msg.

@rs
Copy link
Contributor Author

rs commented Mar 12, 2022

With a 500 bytes msg it's 4x slower for instance.

@tmthrgd
Copy link
Collaborator

tmthrgd commented Mar 12, 2022

Even when using the assembly backed for i := range x { x[i] = 0 }?

@rs
Copy link
Contributor Author

rs commented Mar 12, 2022

This is with this construct yes.

@tmthrgd
Copy link
Collaborator

tmthrgd commented Mar 12, 2022

I just noticed we already have typeBitMapLen. I think we should just do something like the following before the main loop:

toZero := msg[off:]
if maxLen := typeBitMapLen(bitmap); maxLen < len(toZero) {
	toZero = toZero[:maxLen]
}
for i := toZero {
	toZero[i] = 0
}

@rs
Copy link
Contributor Author

rs commented Mar 12, 2022

Awersome, I moved to this function. Please review.

Copy link
Collaborator

@tmthrgd tmthrgd left a comment

Choose a reason for hiding this comment

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

Minor change. Otherwise LGTM.

@@ -558,6 +558,16 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
if len(bitmap) == 0 {
return off, nil
}
if off > len(msg) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it's possible for us to ever be called with off > len(msg), so I think this check is unneeded.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Famous last word :)

The first unit test was testing this and would panic without this test. The cost is marginal so I would tend leave it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is too. I’m certain we can never hit this condition, but I’ll defer to the unit test.

Copy link
Collaborator

@tmthrgd tmthrgd left a comment

Choose a reason for hiding this comment

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

LGTM.

@miekg miekg merged commit 57e2e62 into miekg:master Apr 1, 2022
aanm pushed a commit to cilium/dns that referenced this pull request Jul 29, 2022
* Invalid NSEC/3 bitmap on non-zero buffer

If the PackBuffer is used to encode an NSEC/3 record, the bitmap is
xored with the content of the buffer instead of being zeroed first.

The algorithm has been changed so it is able zero bytes without
losing too much performance (around 2x slower).

* Add some comments + rename some vars to make algo clearer

* Revert to previous algo with window length compute+0 on new window

* Use typeBitMapLen to compute the bitmap length to zero
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

3 participants