Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spanify woff2_dec #178

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAG}")

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAG}")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 20)

# Set search path for our private/public headers as well as Brotli headers
include_directories("src" "include"
Expand Down Expand Up @@ -108,6 +108,9 @@ add_library(convert_woff2ttf_fuzzer STATIC src/convert_woff2ttf_fuzzer.cc)
target_link_libraries(convert_woff2ttf_fuzzer woff2dec)
add_library(convert_woff2ttf_fuzzer_new_entry STATIC src/convert_woff2ttf_fuzzer_new_entry.cc)
target_link_libraries(convert_woff2ttf_fuzzer_new_entry woff2dec)
add_library(enc_dec_fuzzer STATIC src/enc_dec_fuzzer.cc)
target_link_libraries(enc_dec_fuzzer woff2dec woff2enc)
# clang++ -fsanitize=fuzzer -o enc_dec_fuzzer libwoff2common.so libwoff2dec.so libwoff2enc.so libenc_dec_fuzzer.a

# PC files
include(CMakeParseArguments)
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ endif


CFLAGS += $(COMMON_FLAGS)
CXXFLAGS += $(COMMON_FLAGS) -std=c++11
CXXFLAGS += $(COMMON_FLAGS) -std=c++20

SRCDIR = src

Expand All @@ -39,7 +39,7 @@ COMMONOBJ = $(BROTLIOBJ)/common/*.o
OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ))
EXECUTABLES=woff2_compress woff2_decompress woff2_info
EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES))
ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry
ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry enc_dec_fuzzer
ARCHIVE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(ARCHIVES))

ifeq (,$(wildcard $(BROTLI)/*))
Expand Down
71 changes: 45 additions & 26 deletions src/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ typedef unsigned __int64 uint64_t;
#include <cstdlib>
#include <cstring>
#include <limits>
#include <span>

namespace woff2 {

Expand All @@ -58,31 +59,32 @@ inline bool Failure(const char *f, int l, const char *fn) {
class Buffer {
public:
Buffer(const uint8_t *data, size_t len)
: buffer_(data),
length_(len),
: buffer_(data, len),
offset_(0) { }
Buffer(std::span<const uint8_t> data) : buffer_(data), offset_(0) { }

bool Skip(size_t n_bytes) {
return Read(NULL, n_bytes);
return Read(std::span<uint8_t>(), n_bytes);
}

bool Read(uint8_t *data, size_t n_bytes) {
bool Read(std::span<uint8_t> data, size_t n_bytes) {
if (n_bytes > 1024 * 1024 * 1024) {
return FONT_COMPRESSION_FAILURE();
}
if ((offset_ + n_bytes > length_) ||
(offset_ > length_ - n_bytes)) {
if (n_bytes > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
if (data) {
std::memcpy(data, buffer_ + offset_, n_bytes);
if (data.size() != 0) {
std::span<const uint8_t> buffer_view =
remaining_buffer().subspan(0, n_bytes);
std::copy(buffer_view.begin(), buffer_view.end(), data.begin());
}
offset_ += n_bytes;
return true;
}

inline bool ReadU8(uint8_t *value) {
if (offset_ + 1 > length_) {
if (1 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
*value = buffer_[offset_];
Expand All @@ -91,11 +93,11 @@ class Buffer {
}

bool ReadU16(uint16_t *value) {
if (offset_ + 2 > length_) {
if (2 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
*value = ntohs(*value);
*value = static_cast<uint32_t>(buffer_[offset_]) << 8 |
static_cast<uint32_t>(buffer_[offset_ + 1]);
offset_ += 2;
return true;
}
Expand All @@ -105,7 +107,7 @@ class Buffer {
}

bool ReadU24(uint32_t *value) {
if (offset_ + 3 > length_) {
if (3 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
*value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
Expand All @@ -116,11 +118,13 @@ class Buffer {
}

bool ReadU32(uint32_t *value) {
if (offset_ + 4 > length_) {
if (4 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
*value = ntohl(*value);
*value = static_cast<uint32_t>(buffer_[offset_]) << 24 |
static_cast<uint32_t>(buffer_[offset_ + 1]) << 16 |
static_cast<uint32_t>(buffer_[offset_ + 2]) << 8 |
static_cast<uint32_t>(buffer_[offset_ + 3]);
offset_ += 4;
return true;
}
Expand All @@ -130,32 +134,47 @@ class Buffer {
}

bool ReadTag(uint32_t *value) {
if (offset_ + 4 > length_) {
if (4 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
*value = static_cast<uint32_t>(buffer_[offset_]) |
static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
static_cast<uint32_t>(buffer_[offset_ + 2]) << 16 |
static_cast<uint32_t>(buffer_[offset_ + 3]) << 24;
offset_ += 4;
return true;
}

bool ReadR64(uint64_t *value) {
if (offset_ + 8 > length_) {
if (8 > remaining_length()) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
*value = static_cast<uint64_t>(buffer_[offset_]) << 56 |
static_cast<uint64_t>(buffer_[offset_ + 1]) << 48 |
static_cast<uint64_t>(buffer_[offset_ + 2]) << 40 |
static_cast<uint64_t>(buffer_[offset_ + 3]) << 32 |
static_cast<uint64_t>(buffer_[offset_ + 4]) << 24 |
static_cast<uint64_t>(buffer_[offset_ + 5]) << 16 |
static_cast<uint64_t>(buffer_[offset_ + 6]) << 8 |
static_cast<uint64_t>(buffer_[offset_ + 7]);
offset_ += 8;
return true;
}

const uint8_t *buffer() const { return buffer_; }
size_t offset() const { return offset_; }
size_t length() const { return length_; }
inline std::span<const uint8_t> remaining_buffer() const {
return buffer_.subspan(offset_);
};
inline size_t remaining_length() const { return remaining_buffer().size(); }

inline size_t offset() { return offset_; }

void set_offset(size_t newoffset) { offset_ = newoffset; }
void set_offset(size_t newoffset) {
offset_ = newoffset;
}

private:
const uint8_t * const buffer_;
const size_t length_;
// A view of the unowned buffer.
const std::span<const uint8_t> buffer_;
size_t offset_;
};

Expand Down
52 changes: 52 additions & 0 deletions src/enc_dec_fuzzer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <woff2/decode.h>
#include <woff2/encode.h>

#include <string>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t data_size) {
size_t encoded_size = woff2::MaxWOFF2CompressedSize(data, data_size);
std::string encoded(encoded_size, 0);
uint8_t* encoded_data = reinterpret_cast<uint8_t*>(&encoded[0]);

woff2::WOFF2Params params;
if (!woff2::ConvertTTFToWOFF2(data, data_size, encoded_data, &encoded_size,
params)) {
// Do not record this in the corpus
return -1;
}
encoded.resize(encoded_size);

// Decode using newer entry pattern.
// Same pattern as woff2_decompress.
std::string decoded_output(
std::min(woff2::ComputeWOFF2FinalSize(encoded_data, encoded.size()),
woff2::kDefaultMaxSize),
0);
woff2::WOFF2StringOut out(&decoded_output);
woff2::ConvertWOFF2ToTTF(encoded_data, encoded.size(), &out);

// Convert back to encoded version.
size_t re_encoded_size = encoded_size;
std::string re_encoded(re_encoded_size, 0);
uint8_t* re_encoded_data = reinterpret_cast<uint8_t*>(&re_encoded[0]);
if (!woff2::ConvertTTFToWOFF2(
reinterpret_cast<const uint8_t*>(decoded_output.data()),
decoded_output.size(), re_encoded_data, &re_encoded_size, params)) {
fprintf(stderr, "Compression failed.\n");
return -1;
}
re_encoded.resize(re_encoded_size);

// Compressed data == compressed/decompressed/compressed data.
// Note that compressed/decompressed may not be the same as the original data
// provided by libfuzzer because our decompression may output a slightly
// different but functionally equivalent TTF file.
if (encoded_size != re_encoded_size) {
__builtin_trap();
}
if (memcmp(encoded.data(), re_encoded.data(), encoded_size) != 0) {
__builtin_trap();
}

return 0;
}
2 changes: 1 addition & 1 deletion src/glyph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;

bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) {
glyph->have_instructions = false;
glyph->composite_data = buffer->buffer() + buffer->offset();
glyph->composite_data = buffer->remaining_buffer().data();
size_t start_offset = buffer->offset();
uint16_t flags = kFLAG_MORE_COMPONENTS;
while (flags & kFLAG_MORE_COMPONENTS) {
Expand Down
2 changes: 1 addition & 1 deletion src/normalize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ bool FixChecksums(Font* font) {
if (table->IsReused()) {
table = table->reuse_of;
}
table->checksum = ComputeULongSum(table->data, table->length);
table->checksum = ComputeULongSum(std::span(table->data, table->length));
file_checksum += table->checksum;

if (table->tag == kHeadTableTag) {
Expand Down
37 changes: 31 additions & 6 deletions src/store_bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,65 @@
#include <stddef.h>
#include <string.h>

#include <span>

#include "./port.h"

namespace woff2 {

inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
inline size_t StoreU32(std::span<uint8_t> dst, size_t offset, uint32_t x) {
dst[offset] = x >> 24;
dst[offset + 1] = x >> 16;
dst[offset + 2] = x >> 8;
dst[offset + 3] = x;
return offset + 4;
}

inline size_t Store16(uint8_t* dst, size_t offset, int x) {
inline size_t Store16(std::span<uint8_t> dst, size_t offset, int x) {
dst[offset] = x >> 8;
dst[offset + 1] = x;
return offset + 2;
}

inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) {
inline void StoreU32(uint32_t val, size_t* offset, std::span<uint8_t> dst) {
dst[(*offset)++] = val >> 24;
dst[(*offset)++] = val >> 16;
dst[(*offset)++] = val >> 8;
dst[(*offset)++] = val;
}

inline void Store16(int val, size_t* offset, uint8_t* dst) {
inline void Store16(int val, size_t* offset, std::span<uint8_t> dst) {
dst[(*offset)++] = val >> 8;
dst[(*offset)++] = val;
}

inline void StoreBytes(std::span<const uint8_t> data,
size_t* offset, std::span<uint8_t> dst) {
std::copy(data.begin(), data.end(), &dst[*offset]);
*offset += data.size_bytes();
}

// Same as above, but with less safety.

inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
return StoreU32(std::span(dst, sizeof(uint32_t)), offset, x);
}

inline size_t Store16(uint8_t* dst, size_t offset, int x) {
return Store16(std::span(dst, sizeof(uint16_t)), offset, x);
}

inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) {
StoreU32(val, offset, std::span(dst, sizeof(uint32_t)));
}

inline void Store16(int val, size_t* offset, uint8_t* dst) {
return Store16(val, offset, std::span(dst, sizeof(uint16_t)));
}

inline void StoreBytes(const uint8_t* data, size_t len,
size_t* offset, uint8_t* dst) {
memcpy(&dst[*offset], data, len);
*offset += len;
StoreBytes(std::span(data, len), offset, std::span(dst, len));
}

} // namespace woff2
Expand Down
10 changes: 5 additions & 5 deletions src/woff2_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@
/* Helpers common across multiple parts of woff2 */

#include <algorithm>
#include <span>

#include "./woff2_common.h"

#include "./port.h"

namespace woff2 {


uint32_t ComputeULongSum(const uint8_t* buf, size_t size) {
uint32_t ComputeULongSum(std::span<const uint8_t> buf) {
uint32_t checksum = 0;
size_t aligned_size = size & ~3;
size_t aligned_size = buf.size() & ~3;
for (size_t i = 0; i < aligned_size; i += 4) {
checksum +=
(buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3];
}

// treat size not aligned on 4 as if it were padded to 4 with 0's
if (size != aligned_size) {
if (buf.size() != aligned_size) {
uint32_t v = 0;
for (size_t i = aligned_size; i < size; ++i) {
for (size_t i = aligned_size; i < buf.size(); ++i) {
v |= buf[i] << (24 - 8 * (i & 3));
}
checksum += v;
Expand Down
5 changes: 3 additions & 2 deletions src/woff2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stddef.h>
#include <inttypes.h>

#include <span>
#include <string>

namespace woff2 {
Expand Down Expand Up @@ -56,8 +57,8 @@ struct Table {
// True Type Collections
size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts);

// Compute checksum over size bytes of buf
uint32_t ComputeULongSum(const uint8_t* buf, size_t size);
// Compute checksum over buf
uint32_t ComputeULongSum(std::span<const uint8_t> buf);

} // namespace woff2

Expand Down
Loading