aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaph Levien <raph@google.com>2013-04-05 17:01:50 -0700
committerRaph Levien <raph@google.com>2013-04-05 17:01:50 -0700
commitbd9d6ce3ae8b182e717051806be476fe3aa48916 (patch)
tree78ae04a476748362ba5f7f0795323175592a01eb
parent1fec2c0a614079fca28bf76972b3d0395293bc0f (diff)
downloadsrc-bd9d6ce3ae8b182e717051806be476fe3aa48916.tar.gz
Security fixes to woff2 decompression
This patch addresses the security and other problems found by agl in his review of a previous version of the code.
-rw-r--r--cpp/woff2.cc46
1 files changed, 21 insertions, 25 deletions
diff --git a/cpp/woff2.cc b/cpp/woff2.cc
index cc64185..6fe543c 100644
--- a/cpp/woff2.cc
+++ b/cpp/woff2.cc
@@ -66,11 +66,14 @@ const size_t kWoff2EntrySize = 20;
const size_t kLzmaHeaderSize = 13;
+// Compression type values common to both short and long formats
const uint32_t kCompressionTypeMask = 0xf;
const uint32_t kCompressionTypeNone = 0;
const uint32_t kCompressionTypeGzip = 1;
const uint32_t kCompressionTypeLzma = 2;
-// agl: This doesn't seem to match the value in the spec, which is 16.
+
+// This is a special value for the short format only, as described in
+// "Design for compressed header format" in draft doc.
const uint32_t kShortFlagsContinue = 3;
struct Point {
@@ -174,10 +177,7 @@ size_t Store16(uint8_t* dst, size_t offset, int x) {
}
int WithSign(int flag, int baseval) {
- // agl: it isn't generally possible to negate an arbitary integer. (For
- // example, -2137483648 cannot be negated.) However, I believe that the range
- // is 0 <= baseval < 65536 in which case it's safe. If you agree then there
- // should be a comment documenting the precondition of this function.
+ // Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
return (flag & 1) ? baseval : -baseval;
}
@@ -237,7 +237,7 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
(in[triplet_index + 2] << 8) + in[triplet_index + 3]);
}
triplet_index += n_data_bytes;
- // agl: these additions can overflow, although I'm not sure that we care.
+ // Possible overflow but coordinate values are not security sensitive
x += dx;
y += dy;
result->push_back(Point());
@@ -315,10 +315,10 @@ bool StorePoints(const std::vector<Point>& points,
}
dst[flag_offset++] = repeat_count;
}
- // agl: overflow checks can't have multiple additions in them.
- if (flag_offset + x_bytes < flag_offset ||
- flag_offset + x_bytes + y_bytes < flag_offset + x_bytes ||
- flag_offset + x_bytes + y_bytes > dst_size) {
+ unsigned int xy_bytes = x_bytes + y_bytes;
+ if (xy_bytes < x_bytes ||
+ flag_offset + xy_bytes < flag_offset ||
+ flag_offset + xy_bytes > dst_size) {
return OTS_FAILURE();
}
@@ -333,8 +333,7 @@ bool StorePoints(const std::vector<Point>& points,
} else if (dx > -256 && dx < 256) {
dst[x_offset++] = std::abs(dx);
} else {
- // agl: this can go wrong if dx doesn't fit in an int16_t, but it's not
- // clear if that matters.
+ // will always fit for valid input, but overflow is harmless
x_offset = Store16(dst, x_offset, dx);
}
last_x += dx;
@@ -381,8 +380,10 @@ bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
size_t glyf_buf_length) {
const uint8_t* buf = bbox_stream->buffer();
- // agl: I belive that n_glyphs is < 65536, making the next line safe. But a
- // comment and/or assert is needed to document that.
+ if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+ return OTS_FAILURE();
+ }
+ // Safe because n_glyphs is bounded
unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
if (!bbox_stream->Skip(bitmap_length)) {
return OTS_FAILURE();
@@ -390,8 +391,6 @@ bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
for (unsigned int i = 0; i < n_glyphs; ++i) {
if (buf[i >> 3] & (0x80 >> (i & 7))) {
uint32_t loca_offset = loca_values[i];
- // agl: again, I believe that this is safe, but the fact that loca_values
- // has n_glyphs+1 elements should be documented.
if (loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset) {
return OTS_FAILURE();
}
@@ -490,11 +489,10 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
return OTS_FAILURE();
}
unsigned int offset = (2 + kNumSubStreams) * 4;
- // agl: data_size must be >= offset, but this condition isn't established
- // initially.
if (offset > data_size) {
return OTS_FAILURE();
}
+ // Invariant from here on: data_size >= offset
for (int i = 0; i < kNumSubStreams; ++i) {
uint32_t substream_size;
if (!file.ReadU32(&substream_size)) {
@@ -588,7 +586,9 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
int end_point = -1;
for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
end_point += n_points_vec[contour_ix];
- // agl: this fails if end_point > 65535, does that matter?
+ if (end_point >= 65536) {
+ return OTS_FAILURE();
+ }
offset = Store16(glyf_dst, offset, end_point);
}
if (!flag_stream.Skip(flag_size)) {
@@ -685,10 +685,7 @@ bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
uint32_t checksum = 0;
for (size_t i = 0; i < size; i += 4) {
- // We assume the addition is mod 2^32. This is a pretty safe assumption,
- // but technically it's undefined behavior.
- // agl: in reference to the comment above: when using uint32_t, the fact
- // that addition is mod 2^32 is well defined.
+ // We assume the addition is mod 2^32, which is valid because unsigned
checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
(buf[i + 2] << 8) | buf[i + 3];
}
@@ -726,8 +723,7 @@ bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
uLongf uncompressed_length = dst_size;
int r = uncompress(reinterpret_cast<Bytef *>(dst_buf), &uncompressed_length,
src_buf, src_size);
- // agl: I think src_size should be dst_size in the next line.
- if (r != Z_OK || uncompressed_length != src_size) {
+ if (r != Z_OK || uncompressed_length != dst_size) {
return OTS_FAILURE();
}
return true;