1
0
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:
David Steele
2019-04-18 21:24:10 -04:00
parent 2d73de1d36
commit 7390952d8e
5 changed files with 31 additions and 10 deletions

View File

@ -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>

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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)