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

Overlong message chunk in secret stream push causes "double free or corruption" VM crash #81

Open
timmc opened this issue Aug 28, 2020 · 6 comments

Comments

@timmc
Copy link
Contributor

timmc commented Aug 28, 2020

I was getting VM crashes after some code changes where I messed up buffer sizes, and I've narrowed it down to this simplified repro case (in Kotlin):

fun repro() {
    val sodium: LazySodiumJava by lazy { LazySodiumJava(SodiumJava()) }
    val STREAM_CHUNK_BYTES = 4000

    val key = (sodium as SecretStream.Lazy).cryptoSecretStreamKeygen()
    val stream = (sodium as SecretStream.Native)
    val state = SecretStream.State()
    val header = ByteArray(SecretStream.HEADERBYTES)
    stream.cryptoSecretStreamInitPush(state, header, key.asBytes)
    || throw IOException("error")
    val cipherChunk = ByteArray(STREAM_CHUNK_BYTES + SecretStream.ABYTES)

    // This triggers the bug
    val msgChunkTooBig = ByteArray(STREAM_CHUNK_BYTES + 2000)

    // Bad call
    stream.cryptoSecretStreamPush(
        state, cipherChunk,
        msgChunkTooBig, msgChunkTooBig.size.toLong(),
        SecretStream.TAG_MESSAGE
    ) || throw IOException("error")
}

It could probably be simplified more, but it looks like cryptoSecretStreamPush doesn't check byte array sizes before calling libsodium, and libsodium doesn't check either. I think what's going on is that if the message array is too large, libsodium ends up writing past the end of the cipher array and causing memory corruption. I suppose this is similar to issue #64.

@timmc
Copy link
Contributor Author

timmc commented Aug 29, 2020

I'd be up for adding some additional checks similar to #66 but I wonder if there might be a more terse way of doing it. Maybe calls like assertArrayBounds(fooBytes, fooLength) that would be implemented like

public void assertArrayBounds(byte[] array, int len) {
    if(len < 0) throw new IllegalArgumentException("Out of bounds: Negative length");
    if(len > array.length) throw new IllegalArgumentException("Out of bounds: Length greater than array length");
}

It might also make sense to switch some of the Checker stuff over to throw rather than validate, but I haven't looked closely at how that's currently used.

@gurpreet-
Copy link
Contributor

This looks to be an awesome suggestion!

Libsodium does do checks... sometimes... but it can't do everything 😀 Writing C libraries is a difficult endeavour. Porting those C libs to use in another language is even more difficult! I suppose what I'm trying (and failing) to say is that Lazysodium's job is to make up for Libsodium's downsides 🙂

If you could write some functionality in just as you suggested then I can merge it in. Feel free to decline and I'll add it to my to-do list 🙂

@timmc
Copy link
Contributor Author

timmc commented Dec 2, 2020

OK, I'll see if I can work up a PR!

@timmc
Copy link
Contributor Author

timmc commented Dec 3, 2020

Before I go too far down this road, does this kind of change look like something you'd be happy with? timmc/lazysodium-java@test-fast-random...more-bounds-checks

@gurpreet-
Copy link
Contributor

Yes that's perfect 👍

timmc added a commit to timmc/lazysodium-java that referenced this issue Feb 4, 2021
This comes out of issue terl#81 in which a call to `cryptoSecretStreamPush`
with an incorrectly sized array lead to a VM crash.

- Add array checkers to BaseChecker and use in LazySodium
- Simplify some existing checks by moving the `throw` into the check
  method; add reporting of which variable failed a check
- Add checkers for SecretStream
- Split PwHash.Checker.checkAll into individual check calls to reduce
  chance of positional argument errors
timmc added a commit to timmc/lazysodium-java that referenced this issue Feb 8, 2021
This is not comprehensive coverage, but should take care of a number of
unguarded calls. It comes out of issue terl#81 in which a call to
`cryptoSecretStreamPush` with an incorrectly sized array lead to a VM
crash.

- Add array checkers to BaseChecker
- Simplify some existing checks by moving the `throw` into the check
  method; add reporting of which variable failed a check
- Add checkers for SecretStream
- Split PwHash.Checker.checkAll into individual check calls to reduce
  chance of positional argument errors
timmc added a commit to timmc/lazysodium-java that referenced this issue Feb 8, 2021
This is not comprehensive coverage, but should take care of a number of
unguarded calls. It comes out of issue terl#81 in which a call to
`cryptoSecretStreamPush` with an incorrectly sized array lead to a VM
crash.

- Add array checkers to BaseChecker
- Simplify some existing checks by moving the `throw` into the check
  method; add reporting of which variable failed a check
- Add checkers for SecretStream
- Split PwHash.Checker.checkAll into individual check calls to reduce
  chance of positional argument errors
@timmc
Copy link
Contributor Author

timmc commented Apr 16, 2021

I created a PR to add checks: #95 However, it's failing on what I think is an existing incorrect test or implementation -- could you check it out?

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

No branches or pull requests

2 participants