mirror of
https://github.com/qpdf/qpdf.git
synced 2024-11-04 20:37:50 +00:00
d71f05ca07
This makes all integer type conversions that have potential data loss explicit with calls that do range checks and raise an exception. After this commit, qpdf builds with no warnings when -Wsign-conversion -Wconversion is used with gcc or clang or when -W3 -Wd4800 is used with MSVC. This significantly reduces the likelihood of potential crashes from bogus integer values. There are some parts of the code that take int when they should take size_t or an offset. Such places would make qpdf not support files with more than 2^31 of something that usually wouldn't be so large. In the event that such a file shows up and is valid, at least qpdf would raise an error in the right spot so the issue could be legitimately addressed rather than failing in some weird way because of a silent overflow condition.
151 lines
3.4 KiB
Plaintext
151 lines
3.4 KiB
Plaintext
|
|
#ifndef __BITS_CC__
|
|
#define __BITS_CC__
|
|
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <qpdf/QTC.hh>
|
|
#include <qpdf/Pipeline.hh>
|
|
|
|
// These functions may be run at places where the function call
|
|
// overhead from test coverage testing would be too high. Therefore,
|
|
// we make the test coverage cases conditional upon a preprocessor
|
|
// symbol. BitStream.cc includes this file without defining the
|
|
// symbol, and the specially designed test code that fully exercises
|
|
// this code includes with the symbol defined.
|
|
|
|
#ifdef BITS_READ
|
|
static unsigned long long
|
|
read_bits(unsigned char const*& p, size_t& bit_offset,
|
|
size_t& bits_available, size_t bits_wanted)
|
|
{
|
|
// View p as a stream of bits:
|
|
|
|
// 76543210 76543210 ....
|
|
|
|
// bit_offset is the bit number within the first byte that marks
|
|
// the first bit that we would read.
|
|
|
|
if (bits_wanted > bits_available)
|
|
{
|
|
throw std::length_error("overflow reading bit stream");
|
|
}
|
|
if (bits_wanted > 32)
|
|
{
|
|
throw std::out_of_range("read_bits: too many bits requested");
|
|
}
|
|
|
|
unsigned long result = 0;
|
|
#ifdef BITS_TESTING
|
|
if (bits_wanted == 0)
|
|
{
|
|
QTC::TC("libtests", "bits zero bits wanted");
|
|
}
|
|
#endif
|
|
while (bits_wanted > 0)
|
|
{
|
|
// Grab bits from the first byte clearing anything before
|
|
// bit_offset.
|
|
unsigned char byte = static_cast<unsigned char>(
|
|
*p & ((1U << (bit_offset + 1U)) - 1U));
|
|
|
|
// There are bit_offset + 1 bits available in the first byte.
|
|
size_t to_copy = std::min(bits_wanted, bit_offset + 1);
|
|
size_t leftover = (bit_offset + 1) - to_copy;
|
|
|
|
#ifdef BITS_TESTING
|
|
QTC::TC("libtests", "bits bit_offset",
|
|
((bit_offset == 0) ? 0 :
|
|
(bit_offset == 7) ? 1 :
|
|
2));
|
|
QTC::TC("libtests", "bits leftover", (leftover > 0) ? 1 : 0);
|
|
#endif
|
|
|
|
// Right shift so that all the bits we want are right justified.
|
|
byte = static_cast<unsigned char>(byte >> leftover);
|
|
|
|
// Copy the bits into result
|
|
result <<= to_copy;
|
|
result |= byte;
|
|
|
|
// Update pointers
|
|
if (leftover)
|
|
{
|
|
bit_offset = leftover - 1;
|
|
}
|
|
else
|
|
{
|
|
bit_offset = 7;
|
|
++p;
|
|
}
|
|
bits_wanted -= to_copy;
|
|
bits_available -= to_copy;
|
|
|
|
#ifdef BITS_TESTING
|
|
QTC::TC("libtests", "bits iterations",
|
|
((bits_wanted > 8) ? 0 :
|
|
(bits_wanted > 0) ? 1 :
|
|
2));
|
|
#endif
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BITS_WRITE
|
|
static void
|
|
write_bits(unsigned char& ch, size_t& bit_offset,
|
|
unsigned long long val, size_t bits, Pipeline* pipeline)
|
|
{
|
|
if (bits > 32)
|
|
{
|
|
throw std::out_of_range("write_bits: too many bits requested");
|
|
}
|
|
|
|
// bit_offset + 1 is the number of bits left in ch
|
|
#ifdef BITS_TESTING
|
|
if (bits == 0)
|
|
{
|
|
QTC::TC("libtests", "bits write zero bits");
|
|
}
|
|
#endif
|
|
while (bits > 0)
|
|
{
|
|
size_t bits_to_write = std::min(bits, bit_offset + 1);
|
|
unsigned char newval = static_cast<unsigned char>(
|
|
(val >> (bits - bits_to_write)) & ((1U << bits_to_write) - 1));
|
|
size_t bits_left_in_ch = bit_offset + 1 - bits_to_write;
|
|
newval = static_cast<unsigned char>(newval << bits_left_in_ch);
|
|
ch |= newval;
|
|
if (bits_left_in_ch == 0)
|
|
{
|
|
#ifdef BITS_TESTING
|
|
QTC::TC("libtests", "bits write pipeline");
|
|
#endif
|
|
pipeline->write(&ch, 1);
|
|
bit_offset = 7;
|
|
ch = 0;
|
|
}
|
|
else
|
|
{
|
|
#ifdef BITS_TESTING
|
|
QTC::TC("libtests", "bits write leftover");
|
|
#endif
|
|
bit_offset -= bits_to_write;
|
|
}
|
|
bits -= bits_to_write;
|
|
#ifdef BITS_TESTING
|
|
QTC::TC("libtests", "bits write iterations",
|
|
((bits > 8) ? 0 :
|
|
(bits > 0) ? 1 :
|
|
2));
|
|
#endif
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif // __BITS_CC__
|