Check that dest is valid for decompression#3555
Conversation
1c0724e to
a51779d
Compare
a51779d to
4a871a5
Compare
There was a problem hiding this comment.
Was this file included on purpose? (same question for contrib/linux-kernel/test/test)
There was a problem hiding this comment.
oops, nope!
terrelln
left a comment
There was a problem hiding this comment.
Generally looks good, just want to see Yann's opinion on the uintptr_t.
| srcSize -= seqHSize; | ||
|
|
||
| RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); | ||
| RETURN_ERROR_IF(dstCapacity == 0 && nbSeq > 0, dstSize_tooSmall, "dstCapacity == 0, but nbSeq is non-zero"); |
There was a problem hiding this comment.
nit: We can combine this check with the line 2127. dst may only be NULL if dstCapacity == 0.
|
|
||
| RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); | ||
| RETURN_ERROR_IF(dstCapacity == 0 && nbSeq > 0, dstSize_tooSmall, "dstCapacity == 0, but nbSeq is non-zero"); | ||
| RETURN_ERROR_IF(MEM_64bits() && (uptrval)dst + dstCapacity > (uptrval)-1 - (1 << 20), dstSize_tooSmall, |
There was a problem hiding this comment.
@Cyan4973 what do you think of requiring uintptr_t here? We already do the same in LZ4.
There was a problem hiding this comment.
I'm generally not a fan of uintptr_t, because it's an optional type,
and we learned the hard way that this type breaks on some platforms.
What about the following alternative test ?
#define MAX_ADDRESS (const char*)(size_t)(-1)
#define SAFE_DISTANCE (1 << 20)
if (MAX_ADDRESS - (const char*)dst < SAFE_DISTANCE) { ... }
ff03f42 to
25ca394
Compare
25ca394 to
62dd65f
Compare
|
|
||
| RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); | ||
| RETURN_ERROR_IF((dst == NULL || dstCapacity == 0) && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); | ||
| RETURN_ERROR_IF(MEM_64bits() && (size_t)(-1) - (size_t)dst < (size_t)(1 << 20), dstSize_tooSmall, |
There was a problem hiding this comment.
I'll take it,
but note that this formulation essentially replaces uintptr_t by size_t,
which implies that sizeof(size_t) == sizeof(void*).
This is often true, for "common" platforms, but not always.
See this comment for an example.
In this specific example, where sizeof(void*)==16 while sizeof(size_t)==4,
the proposed test will trigger for any address close enough to a multiple of 4 GB.
To be fair, this is a hard issue to fix "properly" (i.e. without UB), so that's why I accept this fix proposal.
Evaluating the difference between 2 addresses which are not part of the same memory segment is UB to begin with (even if it works fine in a single-plane memory addressing architecture). That makes it hard to check the distance of an address from the end of its address space.
For the same reason, a dangling pointer that randomly points anywhere in memory is also UB, even if there is a second variable that states size == 0. So we are trying to fix at runtime a user-side UB. No wonder it's difficult to fix without triggering UB.
There was a problem hiding this comment.
Yeah I'm not sure of the fully correct way to solve this.
As you said, checking the distance between these two addresses is UB regardless.
I guess the major concern with my solution is that on some unusual platforms where sizeof(size_t) == 8 but sizeof(void*) != 8 we may produce a lot of false positives. Would it be better to not check at all?
There was a problem hiding this comment.
I guess the major concern with my solution is that on some unusual platforms where
sizeof(size_t) == 8butsizeof(void*) != 8we may produce a lot of false positives. Would it be better to not check at all?
That's indeed the question.
It seems that your check dstCapacity==0 should catch the issues described in #3507, and possibly #3506.
In which case, maybe that's enough ?
There was a problem hiding this comment.
So maybe the first check is enough to fix the situation.
The second test is fine as long as sizeof(size_t) == sizeof(void*),
maybe that condition could be checked before triggering the second test.
|
I'll merge it now, so that there is enough time testing before the release |
This PR is a response to issues #3506 and #3507.
It adds some checks to ensure that the
destbuffer passed toZSTD_decompressBlockare valid. In particular, these check that if thedstCapacity == 0then the number of sequences is 0 and that the pointer passed todestis sufficiently small that any permittedmatch + literallength can be added to it without overflowing.This PR additionally adds some fuzzing to target these issues. Specifically, we pass a random
destvalue when fuzzing decompression on a buffer of size 0.