[libzstd] Fix ZSTD_compress2() for multithreaded compression
`ZSTD_compress2()` wouldn't wait for multithreaded compression to finish. We didn't find this because ZSTDMT will block when it can compress all in one go, but it can't do that if it doesn't have enough output space, or if `ZSTD_c_rsyncable` is enabled. Since we will already sometimes block when using `ZSTD_e_end`, I've changed `ZSTD_e_end` and `ZSTD_e_flush` to guarantee maximum forward progress. This simplifies the API, and helps users avoid the easy bug that was made in `ZSTD_compress2()` * Found by the libfuzzer fuzzers. * Added a test case that catches the problem. * I will make the fuzzers sometimes allocate less than `ZSTD_compressBound()` output space.
This commit is contained in:
@@ -4179,18 +4179,28 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
|
||||
/* compression stage */
|
||||
#ifdef ZSTD_MULTITHREAD
|
||||
if (cctx->appliedParams.nbWorkers > 0) {
|
||||
int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end);
|
||||
size_t flushMin;
|
||||
assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */);
|
||||
if (cctx->cParamsChanged) {
|
||||
ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
|
||||
cctx->cParamsChanged = 0;
|
||||
}
|
||||
{ size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
|
||||
do {
|
||||
flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
|
||||
if ( ZSTD_isError(flushMin)
|
||||
|| (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */
|
||||
ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
|
||||
}
|
||||
DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");
|
||||
return flushMin;
|
||||
} }
|
||||
FORWARD_IF_ERROR(flushMin);
|
||||
} while (forceMaxProgress && flushMin != 0 && output->pos < output->size);
|
||||
DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");
|
||||
/* Either we don't require maximum forward progress, we've finished the
|
||||
* flush, or we are out of output space.
|
||||
*/
|
||||
assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size);
|
||||
return flushMin;
|
||||
}
|
||||
#endif
|
||||
FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) );
|
||||
DEBUGLOG(5, "completed ZSTD_compressStream2");
|
||||
|
||||
Reference in New Issue
Block a user