You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Harden IO filters against zero input and optimize zero output case.
Add production checks to ensure no filter gets a zero-size input buffer. Also, optimize the case where a filter returns no output. There's no sense in running downstream filters if they have no new input.
This commit is contained in:
@ -36,6 +36,10 @@
|
||||
</release-bug-list>
|
||||
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<p>Harden IO filters against zero input and optimize zero output case.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Move <code>lockRelease()</code> to the end of <code>exitSafe()</code>.</p>
|
||||
</release-item>
|
||||
|
@ -64,6 +64,7 @@ ioFilterProcessIn(IoFilter *this, const Buffer *input)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(this->interface.in != NULL);
|
||||
CHECK(input != NULL && bufUsed(input) > 0);
|
||||
|
||||
this->interface.in(this->driver, input);
|
||||
|
||||
@ -85,9 +86,11 @@ ioFilterProcessInOut(IoFilter *this, const Buffer *input, Buffer *output)
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(output != NULL);
|
||||
ASSERT(this->interface.inOut != NULL);
|
||||
CHECK(input == NULL || bufUsed(input) > 0);
|
||||
|
||||
this->interface.inOut(this->driver, input, output);
|
||||
|
||||
CHECK(!ioFilterInputSame(this) || bufUsed(output) > 0);
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
@ -269,13 +269,19 @@ ioFilterGroupProcess(IoFilterGroup *this, const Buffer *input, Buffer *output)
|
||||
|
||||
ioFilterProcessInOut(filterData->filter, filterData->input, filterData->output);
|
||||
|
||||
// If inputSame is set then the output buffer for this filter is full and it will need to be pre-processed with
|
||||
// the same input once the output buffer is cleared.
|
||||
if (ioFilterInputSame(filterData->filter))
|
||||
// If there was no output then break out of filter processing because more input is needed
|
||||
if (bufUsed(filterData->output) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Else if inputSame is set then the output buffer for this filter is full and it will need to be re-processed
|
||||
// with the same input once the output buffer is cleared
|
||||
else if (ioFilterInputSame(filterData->filter))
|
||||
{
|
||||
this->inputSame = true;
|
||||
|
||||
// Else clear the buffer if it was locally allocated. If this is an input buffer that was passed in then the
|
||||
// caller is responsible for clearing it.
|
||||
}
|
||||
// Else clear the buffer if it was locally allocated. If the input buffer was passed in then the caller is
|
||||
// responsible for clearing it.
|
||||
else if (filterData->inputLocal != NULL)
|
||||
bufUsedZero(filterData->inputLocal);
|
||||
}
|
||||
|
@ -270,7 +270,6 @@ testRun(void)
|
||||
|
||||
bufUsedZero(decryptBuffer);
|
||||
|
||||
ioFilterProcessInOut(blockDecryptFilter, bufNew(0), decryptBuffer);
|
||||
TEST_ERROR(ioFilterProcessInOut(blockDecryptFilter, NULL, decryptBuffer), CryptoError, "cipher header missing");
|
||||
|
||||
cipherBlockFree(blockDecrypt);
|
||||
|
@ -122,6 +122,7 @@ typedef struct IoTestFilterMultiply
|
||||
{
|
||||
MemContext *memContext;
|
||||
unsigned int flushTotal;
|
||||
bool writeZero;
|
||||
char flushChar;
|
||||
Buffer *multiplyBuffer;
|
||||
unsigned int multiplier;
|
||||
@ -142,11 +143,19 @@ ioTestFilterMultiplyProcess(IoTestFilterMultiply *this, const Buffer *input, Buf
|
||||
ASSERT(output != NULL && bufRemains(output) > 0);
|
||||
|
||||
if (input == NULL)
|
||||
{
|
||||
// Write nothing into the output buffer to make sure the filter processing will skip the remaining filters
|
||||
if (!this->writeZero)
|
||||
{
|
||||
this->writeZero = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char flushZ[] = {this->flushChar, 0};
|
||||
bufCat(output, bufNewC(1, flushZ));
|
||||
this->flushTotal--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->multiplyBuffer == NULL)
|
||||
|
Reference in New Issue
Block a user