-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Check that dest
is valid for decompression
#3555
Check that dest
is valid for decompression
#3555
Conversation
1c0724e
to
a51779d
Compare
a51779d
to
4a871a5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this file included on purpose? (same question for contrib/linux-kernel/test/test
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, nope!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good, just want to see Yann's opinion on the uintptr_t
.
@@ -2125,6 +2125,9 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: We can combine this check with the line 2127
. dst
may only be NULL
if dstCapacity == 0
.
@@ -2125,6 +2125,9 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, | |||
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"); | |||
RETURN_ERROR_IF(MEM_64bits() && (uptrval)dst + dstCapacity > (uptrval)-1 - (1 << 20), dstSize_tooSmall, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Cyan4973 what do you think of requiring uintptr_t
here? We already do the same in LZ4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -2124,7 +2124,9 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, | |||
ip += seqHSize; | |||
srcSize -= seqHSize; | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the major concern with my solution is that on some unusual platforms where
sizeof(size_t) == 8
butsizeof(void*) != 8
we 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
dest
buffer passed toZSTD_decompressBlock
are valid. In particular, these check that if thedstCapacity == 0
then the number of sequences is 0 and that the pointer passed todest
is sufficiently small that any permittedmatch + literal
length can be added to it without overflowing.This PR additionally adds some fuzzing to target these issues. Specifically, we pass a random
dest
value when fuzzing decompression on a buffer of size 0.