Commit 087d4503 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

qr: fix several issues with larger QR codes.

When building larger QR codes, the previous code would read off the end
of a buffer and interleaved blocks incorrectly, thus generating invalid
QR codes.

Change-Id: Id00dcd5c3d5f4bacde281baaa759c3920d664048
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2369258Reviewed-by: default avatarTravis Skare <skare@chromium.org>
Commit-Queue: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801089}
parent 39ec95db
...@@ -48,7 +48,9 @@ struct QRVersionInfo { ...@@ -48,7 +48,9 @@ struct QRVersionInfo {
(version < 7 && encoded_version != 0) || (version < 7 && encoded_version != 0) ||
(version >= 7 && (version >= 7 &&
encoded_version >> 12 != static_cast<uint32_t>(version)) || encoded_version >> 12 != static_cast<uint32_t>(version)) ||
(version <= kMaxVersionWith8BitLength && input_bytes() >= 256)) { (version <= kMaxVersionWith8BitLength && input_bytes() >= 256) ||
(group2_num_blocks != 0 &&
group2_block_ec_bytes() != group1_block_ec_bytes())) {
__builtin_unreachable(); __builtin_unreachable();
} }
} }
...@@ -255,10 +257,10 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -255,10 +257,10 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
if (version_info != version_info_) { if (version_info != version_info_) {
version_info_ = version_info; version_info_ = version_info;
d_ = std::make_unique<uint8_t[]>(version_info_->total_size()); d_.resize(version_info_->total_size());
} }
// Previous data and "set" bits must be cleared. // Previous data and "set" bits must be cleared.
memset(d_.get(), 0, version_info_->total_size()); memset(&d_[0], 0, version_info_->total_size());
PutVerticalTiming(6); PutVerticalTiming(6);
PutHorizontalTiming(6); PutHorizontalTiming(6);
...@@ -309,8 +311,9 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -309,8 +311,9 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
// Details are in Table 3. // Details are in Table 3.
// Since 12 and 20 are not a multiple of eight, a frame-shift of all // Since 12 and 20 are not a multiple of eight, a frame-shift of all
// subsequent bytes is required. // subsequent bytes is required.
size_t data_bytes = version_info_->total_bytes(); const size_t framed_input_size =
uint8_t prefixed_data[data_bytes]; version_info_->group1_data_bytes() + version_info_->group2_data_bytes();
std::vector<uint8_t> prefixed_data(framed_input_size);
size_t framing_offset_bytes = 0; size_t framing_offset_bytes = 0;
if (version_info->version <= kMaxVersionWith8BitLength) { if (version_info->version <= kMaxVersionWith8BitLength) {
DCHECK_LT(in.size(), 0x100u) << "in.size() too large for 8-bit length"; DCHECK_LT(in.size(), 0x100u) << "in.size() too large for 8-bit length";
...@@ -332,19 +335,20 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -332,19 +335,20 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
} }
framing_offset_bytes = 3; framing_offset_bytes = 3;
} }
DCHECK_LE(in.size() + framing_offset_bytes, prefixed_data.size());
for (size_t i = 0; i < in.size() - 1; i++) { for (size_t i = 0; i < in.size() - 1; i++) {
prefixed_data[i + framing_offset_bytes] = (in[i] << 4) | (in[i + 1] >> 4); prefixed_data[i + framing_offset_bytes] = (in[i] << 4) | (in[i + 1] >> 4);
} }
if (!in.empty()) { if (!in.empty()) {
prefixed_data[in.size() + 1] = in[in.size() + 1 - framing_offset_bytes] prefixed_data[in.size() - 1 + framing_offset_bytes] = in[in.size() - 1]
<< 4; << 4;
} }
// The QR code looks a little odd with fixed padding. Thus replicate the // The QR code looks a little odd with fixed padding. Thus replicate the
// message to fill the input. // message to fill the input.
for (size_t i = in.size() + framing_offset_bytes; for (size_t i = in.size() + framing_offset_bytes; i < framed_input_size;
i < version_info_->input_bytes(); i++) { i++) {
prefixed_data[i] = prefixed_data[i % (in.size() + framing_offset_bytes)]; prefixed_data[i] = prefixed_data[i % (in.size() + framing_offset_bytes)];
} }
...@@ -354,20 +358,24 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -354,20 +358,24 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
// Error Correction for Group 1, present for all versions. // Error Correction for Group 1, present for all versions.
const size_t group1_num_blocks = version_info_->group1_num_blocks; const size_t group1_num_blocks = version_info_->group1_num_blocks;
const size_t group1_block_bytes = version_info_->group1_block_bytes(); const size_t group1_block_bytes = version_info_->group1_block_bytes();
const size_t group1_block_data_bytes = version_info_->group1_block_data_bytes;
const size_t group1_block_ec_bytes = version_info_->group1_block_ec_bytes(); const size_t group1_block_ec_bytes = version_info_->group1_block_ec_bytes();
uint8_t expanded_blocks[group1_num_blocks][group1_block_bytes]; std::vector<std::vector<uint8_t>> expanded_blocks(group1_num_blocks);
for (size_t i = 0; i < group1_num_blocks; i++) { for (size_t i = 0; i < group1_num_blocks; i++) {
expanded_blocks[i].resize(group1_block_bytes);
AddErrorCorrection( AddErrorCorrection(
&expanded_blocks[i][0], expanded_blocks[i],
&prefixed_data[version_info_->group1_block_data_bytes * i], base::span<const uint8_t>(&prefixed_data[group1_block_data_bytes * i],
group1_block_data_bytes),
group1_block_bytes, group1_block_ec_bytes); group1_block_bytes, group1_block_ec_bytes);
} }
// Error Correction for Group 2, present for some versions. // Error Correction for Group 2, present for some versions.
// Factor out the number of bytes written by the prior group. // Factor out the number of bytes written by the prior group.
const size_t group_data_offset = group1_block_bytes * group1_num_blocks; const size_t group_data_offset = version_info_->group1_data_bytes();
const size_t group2_num_blocks = version_info_->group2_num_blocks; const size_t group2_num_blocks = version_info_->group2_num_blocks;
const size_t group2_block_bytes = version_info_->group2_block_bytes(); const size_t group2_block_bytes = version_info_->group2_block_bytes();
const size_t group2_block_data_bytes = version_info_->group2_block_data_bytes;
const size_t group2_block_ec_bytes = version_info_->group2_block_ec_bytes(); const size_t group2_block_ec_bytes = version_info_->group2_block_ec_bytes();
std::vector<std::vector<uint8_t>> expanded_blocks_2; std::vector<std::vector<uint8_t>> expanded_blocks_2;
...@@ -376,9 +384,10 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -376,9 +384,10 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
for (size_t i = 0; i < group2_num_blocks; i++) { for (size_t i = 0; i < group2_num_blocks; i++) {
expanded_blocks_2[i].resize(group2_block_bytes); expanded_blocks_2[i].resize(group2_block_bytes);
AddErrorCorrection( AddErrorCorrection(
&expanded_blocks_2[i][0], expanded_blocks_2[i],
&prefixed_data[group_data_offset + base::span<const uint8_t>(
version_info_->group2_block_data_bytes * i], &prefixed_data[group_data_offset + group2_block_data_bytes * i],
group2_block_data_bytes),
group2_block_bytes, group2_block_ec_bytes); group2_block_bytes, group2_block_ec_bytes);
} }
} }
...@@ -390,24 +399,47 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -390,24 +399,47 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
<< "internal error"; << "internal error";
size_t k = 0; size_t k = 0;
// Interleave data from all blocks. // Interleave data from all blocks. All non-ECC data is written before any ECC
// If we have multiple groups, the later groups may have more bytes in their // data. The group two blocks, if any, will be longer than the group one
// blocks after we exhaust data in the first group. // blocks. Once group one is exhausted then the interleave considers only
size_t max_group_block_bytes = // group two.
std::max(group1_block_bytes, group2_block_bytes); DCHECK(group2_num_blocks == 0 || version_info_->group2_block_data_bytes >
for (size_t j = 0; j < max_group_block_bytes; j++) { version_info_->group1_block_data_bytes);
if (j < group1_block_bytes) {
for (size_t i = 0; i < group1_num_blocks; i++) { size_t j = 0;
interleaved_data[k++] = expanded_blocks[i][j]; for (; j < version_info_->group1_block_data_bytes; j++) {
} for (size_t i = 0; i < group1_num_blocks; i++) {
interleaved_data[k++] = expanded_blocks[i][j];
}
for (size_t i = 0; i < group2_num_blocks; i++) {
interleaved_data[k++] = expanded_blocks_2[i][j];
} }
if (j < group2_block_bytes) { }
if (group2_num_blocks > 0) {
for (; j < version_info_->group2_block_data_bytes; j++) {
for (size_t i = 0; i < group2_num_blocks; i++) { for (size_t i = 0; i < group2_num_blocks; i++) {
interleaved_data[k++] = expanded_blocks_2[i][j]; interleaved_data[k++] = expanded_blocks_2[i][j];
} }
} }
} }
// The number of ECC bytes in each group is the same so the interleave
// considers them uniformly.
DCHECK(version_info_->group2_num_blocks == 0 ||
version_info_->group2_block_ec_bytes() ==
version_info_->group1_block_ec_bytes());
for (size_t j = 0; j < version_info_->group1_block_ec_bytes(); j++) {
for (size_t i = 0; i < group1_num_blocks; i++) {
interleaved_data[k++] =
expanded_blocks[i][version_info_->group1_block_data_bytes + j];
}
for (size_t i = 0; i < group2_num_blocks; i++) {
interleaved_data[k++] =
expanded_blocks_2[i][version_info_->group2_block_data_bytes + j];
}
}
DCHECK_EQ(k, total_bytes);
// The mask pattern is fixed for this implementation. A full implementation // The mask pattern is fixed for this implementation. A full implementation
// would generate QR codes with every mask pattern and evaluate a quality // would generate QR codes with every mask pattern and evaluate a quality
// score, ultimately picking the optimal pattern. Here it's assumed that a // score, ultimately picking the optimal pattern. Here it's assumed that a
...@@ -416,7 +448,7 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( ...@@ -416,7 +448,7 @@ base::Optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
PutBits(interleaved_data, sizeof(interleaved_data), MaskFunction3); PutBits(interleaved_data, sizeof(interleaved_data), MaskFunction3);
GeneratedCode code; GeneratedCode code;
code.data = base::span<uint8_t>(d_.get(), version_info_->total_size()); code.data = base::span<uint8_t>(&d_[0], version_info_->total_size());
code.qr_size = version_info_->size; code.qr_size = version_info_->size;
return code; return code;
} }
...@@ -674,8 +706,8 @@ uint8_t QRCodeGenerator::GF28Mul(uint16_t a, uint16_t b) { ...@@ -674,8 +706,8 @@ uint8_t QRCodeGenerator::GF28Mul(uint16_t a, uint16_t b) {
// |out|. // |out|.
// |out| should have length block_bytes for the code's version. // |out| should have length block_bytes for the code's version.
// |in| should have length block_data_bytes for the code's version. // |in| should have length block_data_bytes for the code's version.
void QRCodeGenerator::AddErrorCorrection(uint8_t out[], void QRCodeGenerator::AddErrorCorrection(base::span<uint8_t> out,
const uint8_t in[], base::span<const uint8_t> in,
size_t block_bytes, size_t block_bytes,
size_t block_ec_bytes) { size_t block_ec_bytes) {
// kGenerator is the product of (z - x^i) for 0 <= i < |block_ec_bytes|, // kGenerator is the product of (z - x^i) for 0 <= i < |block_ec_bytes|,
...@@ -743,6 +775,7 @@ void QRCodeGenerator::AddErrorCorrection(uint8_t out[], ...@@ -743,6 +775,7 @@ void QRCodeGenerator::AddErrorCorrection(uint8_t out[],
// Multiplication of |in| by x^k thus just involves moving it up. // Multiplication of |in| by x^k thus just involves moving it up.
uint8_t remainder[block_bytes]; uint8_t remainder[block_bytes];
DCHECK_LE(block_ec_bytes, block_bytes);
memset(remainder, 0, block_ec_bytes); memset(remainder, 0, block_ec_bytes);
size_t block_data_bytes = block_bytes - block_ec_bytes; size_t block_data_bytes = block_bytes - block_ec_bytes;
// Reed-Solomon input is backwards. See section 7.5.2. // Reed-Solomon input is backwards. See section 7.5.2.
...@@ -762,7 +795,7 @@ void QRCodeGenerator::AddErrorCorrection(uint8_t out[], ...@@ -762,7 +795,7 @@ void QRCodeGenerator::AddErrorCorrection(uint8_t out[],
} }
} }
memmove(out, in, block_data_bytes); memmove(&out[0], &in[0], block_data_bytes);
// Remove the Reed-Solomon remainder again to match QR's convention. // Remove the Reed-Solomon remainder again to match QR's convention.
for (size_t i = 0; i < block_ec_bytes; i++) { for (size_t i = 0; i < block_ec_bytes; i++) {
out[block_data_bytes + i] = remainder[block_ec_bytes - 1 - i]; out[block_data_bytes + i] = remainder[block_ec_bytes - 1 - i];
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <vector>
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/optional.h" #include "base/optional.h"
...@@ -112,8 +114,8 @@ class QRCodeGenerator { ...@@ -112,8 +114,8 @@ class QRCodeGenerator {
// |in| should have length block_data_bytes for the code's version. // |in| should have length block_data_bytes for the code's version.
// |block_bytes| and |block_ec_bytes| must be provided for the current // |block_bytes| and |block_ec_bytes| must be provided for the current
// version/level/group. // version/level/group.
void AddErrorCorrection(uint8_t out[], void AddErrorCorrection(base::span<uint8_t> out,
const uint8_t in[], base::span<const uint8_t> in,
size_t block_bytes, size_t block_bytes,
size_t block_ec_bytes); size_t block_ec_bytes);
...@@ -127,7 +129,7 @@ class QRCodeGenerator { ...@@ -127,7 +129,7 @@ class QRCodeGenerator {
// is part of the structure of the QR code, i.e. finder or alignment symbols, // is part of the structure of the QR code, i.e. finder or alignment symbols,
// timing patterns, or format data. // timing patterns, or format data.
// Initialized and possibly reinitialized in Generate(). // Initialized and possibly reinitialized in Generate().
std::unique_ptr<uint8_t[]> d_; std::vector<uint8_t> d_;
// clip_dump_ is the target of paints that would otherwise fall outside of the // clip_dump_ is the target of paints that would otherwise fall outside of the
// QR code. // QR code.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment