From ac9c1f0d560540fda821b2775a475c71b47cb3a0 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 5 Oct 2013 19:42:39 -0400 Subject: [PATCH] Security: replace operator[] with at For std::string and std::vector, replace operator[] with at. This was done using an automated process. See README.hardening for details. --- ChangeLog | 5 ++ README.hardening | 93 +++++++++++++++++++++++++ README.maintainer | 3 + examples/pdf-bookmarks.cc | 2 +- examples/pdf-mod-info.cc | 2 +- examples/pdf-parse-content.cc | 2 +- libqpdf/Pl_LZWDecoder.cc | 6 +- libqpdf/QPDF.cc | 12 ++-- libqpdf/QPDFObjectHandle.cc | 14 ++-- libqpdf/QPDFTokenizer.cc | 6 +- libqpdf/QPDFWriter.cc | 6 +- libqpdf/QPDF_Array.cc | 4 +- libqpdf/QPDF_Name.cc | 4 +- libqpdf/QPDF_Stream.cc | 4 +- libqpdf/QPDF_String.cc | 14 ++-- libqpdf/QPDF_encryption.cc | 2 +- libqpdf/QPDF_linearization.cc | 126 +++++++++++++++++----------------- libqpdf/QPDF_optimization.cc | 2 +- libqpdf/QPDF_pages.cc | 8 +-- libqpdf/QUtil.cc | 2 +- qpdf/qpdf.cc | 10 +-- qpdf/test_driver.cc | 76 ++++++++++---------- qpdf/test_large_file.cc | 4 +- 23 files changed, 254 insertions(+), 153 deletions(-) create mode 100644 README.hardening diff --git a/ChangeLog b/ChangeLog index 6ef8d42f..951d9d03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2013-10-05 Jay Berkenbilt + * Replace operator[] in std::string and std::vector with "at" in + order to get bounds checking. This reduces the chances that + incorrect code will result in data exposure or buffer overruns. + See README.hardening for additional notes. + * Use cryptographically secure random number generation when available. See additional notes in README. diff --git a/README.hardening b/README.hardening new file mode 100644 index 00000000..a2389af3 --- /dev/null +++ b/README.hardening @@ -0,0 +1,93 @@ +Avoiding operator[] +=================== + +During a security review by Red Hat security team (specifically +Florian Weimer), it was discovered that qpdf used std::string and +std::vector's operator[], which has no bounds checking by design. +Instead, using those objects' at() method is preferable since it does +bounds checking. Florian has a tool that can detect all uses of these +methods and report them. I have a short perl script that +automatically corrects any such uses. The perl script is not intended +to be general, but it could be reasonably general. The only known +shortcut is that it might not work very well with some cases of nested +[]'s like a[b[c]] or with cases where there are line breaks inside the +brackets. For qpdf's coding style, it worked on all cases reported. + +To use this, obtain htcondor-analyzer, run it, and respond to the +report. Here's what I did. + +sudo aptitude install libclang-dev llvm llvm-dev clang +cd /tmp +git clone https://github.com/fweimer/htcondor-analyzer +# HEAD = 5fa06fc68a9b0677e9de162279185d58ba1e8477 at this writing +cd htcondor-analyzer +make + +in qpdf + +./autogen.sh +/tmp/htcondor-analyzer/create-db +CC=/tmp/htcondor-analyzer/cc CXX=/tmp/htcondor-analyzer/cxx ./configure --disable-shared --disable-werror +# to remove conftest.c +\rm htcondor-analyzer.sqlite +/tmp/htcondor-analyzer/create-db + +Repeat until no more errors: + +/tmp/fix-at.pl is shown below. + +make +/tmp/htcondor-analyzer/report | grep std:: | grep qpdf >| /tmp/r +perl /tmp/fix-at.pl /tmp/r +# move all *.new over the original file. patmv is my script. Can +# also use a for loop. +patmv -f s/.new// **/*.new + +---------- /tmp/fix-at.pl ---------- +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; +use File::Basename; + +my $whoami = basename($0); + +my %to_fix = (); +while (<>) +{ + chomp; + die unless m/^([^:]+):(\d+):(\d+):\s*(.*)$/; + my ($file, $line, $col, $message) = ($1, $2, $3, $4); + if ($message !~ m/operator\[\]/) + { + warn "skipping $_\n"; + next; + } + push(@{$to_fix{$file}}, [$line, $col, $message]); +} +foreach my $file (sort keys %to_fix) +{ + open(F, "<$file") or die; + my @lines = (); + close(F); + my $last = ""; + my @data = reverse sort { ($a->[0] <=> $b->[0]) || ($a->[1] <=> $b->[1]) } @{$to_fix{$file}}; + foreach my $d (@data) + { + my ($line, $col) = @$d; + next if $last eq "$line:$col"; + $last = "$line:$col"; + die if $line-- < 1; + die if $col-- < 1; + print $lines[$line]; + $lines[$line] =~ s/^(.{$col})([^\[]+)\[([^\]]+)\]/$1$2.at($3)/ or die "$file:$last\n"; + print $lines[$line]; + } + open(F, ">$file.new") or die; + foreach my $line (@lines) + { + print F $line; + } + close(F) or die; +} +-------------------- diff --git a/README.maintainer b/README.maintainer index a7f5a3b5..646a9e6c 100644 --- a/README.maintainer +++ b/README.maintainer @@ -43,6 +43,9 @@ Release Reminders casting policy in the manual, and ensure that integer types are properly handled. + * Remember to avoid using operator[] with std::string or + std::vector. See README.hardening for details. + * Increment shared library version information as needed (libqpdf/build.mk) diff --git a/examples/pdf-bookmarks.cc b/examples/pdf-bookmarks.cc index 5d11fb32..23b7f0cf 100644 --- a/examples/pdf-bookmarks.cc +++ b/examples/pdf-bookmarks.cc @@ -31,7 +31,7 @@ void print_lines(std::vector& numbers) { for (unsigned int i = 0; i < numbers.size() - 1; ++i) { - if (numbers[i]) + if (numbers.at(i)) { std::cout << "| "; } diff --git a/examples/pdf-mod-info.cc b/examples/pdf-mod-info.cc index 8bd9f5d6..84cb818f 100644 --- a/examples/pdf-mod-info.cc +++ b/examples/pdf-mod-info.cc @@ -128,7 +128,7 @@ int main(int argc, char* argv[]) { QTC::TC("examples", "pdf-mod-info -key"); cur_key = argv[i]; - if (! ((cur_key.length() > 0) && (cur_key[0] == '/'))) + if (! ((cur_key.length() > 0) && (cur_key.at(0) == '/'))) { cur_key = "/" + cur_key; } diff --git a/examples/pdf-parse-content.cc b/examples/pdf-parse-content.cc index 0394e938..7f11be05 100644 --- a/examples/pdf-parse-content.cc +++ b/examples/pdf-parse-content.cc @@ -74,7 +74,7 @@ int main(int argc, char* argv[]) usage(); } - QPDFObjectHandle page = pages[pageno-1]; + QPDFObjectHandle page = pages.at(pageno-1); QPDFObjectHandle contents = page.getKey("/Contents"); ParserCallbacks cb; QPDFObjectHandle::parseContentStream(contents, &cb); diff --git a/libqpdf/Pl_LZWDecoder.cc b/libqpdf/Pl_LZWDecoder.cc index 82a668e3..2cc2077b 100644 --- a/libqpdf/Pl_LZWDecoder.cc +++ b/libqpdf/Pl_LZWDecoder.cc @@ -109,7 +109,7 @@ Pl_LZWDecoder::getFirstChar(int code) throw std::logic_error( "Pl_LZWDecoder::getFirstChar: table overflow"); } - Buffer& b = table[idx]; + Buffer& b = table.at(idx); result = b.getBuffer()[0]; } else @@ -142,7 +142,7 @@ Pl_LZWDecoder::addToTable(unsigned char next) throw std::logic_error( "Pl_LZWDecoder::addToTable: table overflow"); } - Buffer& b = table[idx]; + Buffer& b = table.at(idx); last_data = b.getBuffer(); last_size = b.getSize(); } @@ -238,7 +238,7 @@ Pl_LZWDecoder::handleCode(int code) } else { - Buffer& b = table[code - 258]; + Buffer& b = table.at(code - 258); getNext()->write(b.getBuffer(), b.getSize()); } } diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 39fd7208..26efaa78 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -531,7 +531,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) // For xref_table, these will always be small enough to be ints qpdf_offset_t f1 = QUtil::string_to_ll(m2.getMatch(1).c_str()); int f2 = atoi(m2.getMatch(2).c_str()); - char type = m2.getMatch(3)[0]; + char type = m2.getMatch(3).at(0); if (type == 'f') { // Save deleted items until after we've checked the @@ -758,17 +758,17 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) long long num_entries = 0; for (unsigned int i = 1; i < indx.size(); i += 2) { - if (indx[i] > max_num_entries - num_entries) + if (indx.at(i) > max_num_entries - num_entries) { throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), "xref stream", xref_offset, "Cross-reference stream claims to contain" " too many entries: " + - QUtil::int_to_string(indx[i]) + " " + + QUtil::int_to_string(indx.at(i)) + " " + QUtil::int_to_string(max_num_entries) + " " + QUtil::int_to_string(num_entries)); } - num_entries += indx[i]; + num_entries += indx.at(i); } // entry_size and num_entries have both been validated to ensure @@ -829,9 +829,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) // based on /Index. The generation number is 0 unless this is // an uncompressed object record, in which case the generation // number appears as the third field. - int obj = indx[cur_chunk] + chunk_count; + int obj = indx.at(cur_chunk) + chunk_count; ++chunk_count; - if (chunk_count >= indx[cur_chunk + 1]) + if (chunk_count >= indx.at(cur_chunk + 1)) { cur_chunk += 2; chunk_count = 0; diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 442678fd..ef92e3ac 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -712,7 +712,7 @@ QPDFObjectHandle::parse(std::string const& object_str, size_t offset = input->tell(); while (offset < object_str.length()) { - if (! isspace(object_str[offset])) + if (! isspace(object_str.at(offset))) { QTC::TC("qpdf", "QPDFObjectHandle trailing data in parse"); throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), @@ -966,8 +966,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, std::string const& value = token.getValue(); if ((value == "R") && (in_array || in_dictionary) && (olist.size() >= 2) && - (olist[olist.size() - 1].isInteger()) && - (olist[olist.size() - 2].isInteger())) + (olist.at(olist.size() - 1).isInteger()) && + (olist.at(olist.size() - 2).isInteger())) { if (context == 0) { @@ -979,8 +979,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, // Try to resolve indirect objects object = newIndirect( context, - olist[olist.size() - 2].getIntValue(), - olist[olist.size() - 1].getIntValue()); + olist.at(olist.size() - 2).getIntValue(), + olist.at(olist.size() - 1).getIntValue()); olist.pop_back(); olist.pop_back(); } @@ -1067,8 +1067,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, } for (unsigned int i = 0; i < olist.size(); i += 2) { - QPDFObjectHandle key_obj = olist[i]; - QPDFObjectHandle val = olist[i + 1]; + QPDFObjectHandle key_obj = olist.at(i); + QPDFObjectHandle val = olist.at(i + 1); if (! key_obj.isName()) { throw QPDFExc( diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc index 19442bab..91b16825 100644 --- a/libqpdf/QPDFTokenizer.cc +++ b/libqpdf/QPDFTokenizer.cc @@ -61,7 +61,7 @@ QPDFTokenizer::resolveLiteral() { PCRE num_re("^[\\+\\-]?(?:\\.\\d+|\\d+(?:\\.\\d+)?)$"); - if ((val.length() > 0) && (val[0] == '/')) + if ((val.length() > 0) && (val.at(0) == '/')) { type = tt_name; // Deal with # in name token. Note: '/' by itself is a @@ -397,8 +397,8 @@ QPDFTokenizer::presentCharacter(char ch) std::string nval; for (unsigned int i = 0; i < val.length(); i += 2) { - num[0] = val[i]; - num[1] = val[i+1]; + num[0] = val.at(i); + num[1] = val.at(i+1); char nch = static_cast(strtol(num, 0, 16)); nval += nch; } diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 949652c5..833637d3 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -1569,7 +1569,7 @@ QPDFWriter::writeObjectStreamOffsets(std::vector& offsets, } writeString(QUtil::int_to_string(i + first_obj)); writeString(" "); - writeString(QUtil::int_to_string(offsets[i])); + writeString(QUtil::int_to_string(offsets.at(i))); } writeString("\n"); } @@ -1603,7 +1603,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) { // Adjust offsets to skip over comment before first object - first = offsets[0]; + first = offsets.at(0); for (std::vector::iterator iter = offsets.begin(); iter != offsets.end(); ++iter) { @@ -2745,7 +2745,7 @@ QPDFWriter::writeLinearized() if (pass == 2) { std::vector const& pages = pdf.getAllPages(); - int first_page_object = obj_renumber[pages[0].getObjGen()]; + int first_page_object = obj_renumber[pages.at(0).getObjGen()]; int npages = pages.size(); writeString(" /Linearized 1 /L "); diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index 00903fa6..c526174f 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -60,7 +60,7 @@ QPDF_Array::getItem(int n) const throw std::logic_error( "INTERNAL ERROR: bounds error accessing QPDF_Array element"); } - return this->items[n]; + return this->items.at(n); } std::vector const& @@ -74,7 +74,7 @@ QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) { // Call getItem for bounds checking (void) getItem(n); - this->items[n] = oh; + this->items.at(n) = oh; } void diff --git a/libqpdf/QPDF_Name.cc b/libqpdf/QPDF_Name.cc index 26bd8263..0c2082c4 100644 --- a/libqpdf/QPDF_Name.cc +++ b/libqpdf/QPDF_Name.cc @@ -21,10 +21,10 @@ QPDF_Name::normalizeName(std::string const& name) return name; } std::string result; - result += name[0]; + result += name.at(0); for (unsigned int i = 1; i < name.length(); ++i) { - char ch = name[i]; + char ch = name.at(i); // Don't use locale/ctype here; follow PDF spec guidelines. if (strchr("#()<>[]{}/%", ch) || (ch < 33) || (ch > 126)) { diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 0a82f22b..5704e83f 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -310,7 +310,7 @@ QPDF_Stream::filterable(std::vector& filters, for (unsigned int i = 0; i < filters.size(); ++i) { - QPDFObjectHandle decode_item = decode_parms[i]; + QPDFObjectHandle decode_item = decode_parms.at(i); if (decode_item.isNull()) { // okay @@ -318,7 +318,7 @@ QPDF_Stream::filterable(std::vector& filters, else if (decode_item.isDictionary()) { if (! understandDecodeParams( - filters[i], decode_item, + filters.at(i), decode_item, predictor, columns, early_code_change)) { filterable = false; diff --git a/libqpdf/QPDF_String.cc b/libqpdf/QPDF_String.cc index 96736613..ca8d3adc 100644 --- a/libqpdf/QPDF_String.cc +++ b/libqpdf/QPDF_String.cc @@ -55,7 +55,7 @@ QPDF_String::unparse(bool force_binary) int consecutive_printable = 0; for (unsigned int i = 0; i < this->val.length(); ++i) { - char ch = this->val[i]; + char ch = this->val.at(i); // Note: do not use locale to determine printability. The // PDF specification accepts arbitrary binary data. Some // locales imply multibyte characters. We'll consider @@ -97,7 +97,7 @@ QPDF_String::unparse(bool force_binary) result += "("; for (unsigned int i = 0; i < this->val.length(); ++i) { - char ch = this->val[i]; + char ch = this->val.at(i); switch (ch) { case '\n': @@ -135,7 +135,7 @@ QPDF_String::unparse(bool force_binary) default: if (is_iso_latin1_printable(ch)) { - result += this->val[i]; + result += this->val.at(i); } else { @@ -164,7 +164,7 @@ QPDF_String::getUTF8Val() const std::string result; size_t len = this->val.length(); if ((len >= 2) && (len % 2 == 0) && - (this->val[0] == '\xfe') && (this->val[1] == '\xff')) + (this->val.at(0) == '\xfe') && (this->val.at(1) == '\xff')) { // This is a Unicode string using big-endian UTF-16. This // code uses unsigned long and unsigned short to hold @@ -181,8 +181,8 @@ QPDF_String::getUTF8Val() const // discarded, and a low codepoint not preceded by a high // codepoint will just get its low 10 bits output. unsigned short bits = - (static_cast(this->val[i]) << 8) + - static_cast(this->val[i+1]); + (static_cast(this->val.at(i)) << 8) + + static_cast(this->val.at(i+1)); if ((bits & 0xFC00) == 0xD800) { codepoint = 0x10000 + ((bits & 0x3FF) << 10); @@ -209,7 +209,7 @@ QPDF_String::getUTF8Val() const { for (unsigned int i = 0; i < len; ++i) { - result += QUtil::toUTF8(static_cast(this->val[i])); + result += QUtil::toUTF8(static_cast(this->val.at(i))); } } return result; diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 88cd707f..b6a035ef 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -312,7 +312,7 @@ hash_V5(std::string const& password, int E_mod_3 = 0; for (unsigned int i = 0; i < 16; ++i) { - E_mod_3 += static_cast(E[i]); + E_mod_3 += static_cast(E.at(i)); } E_mod_3 %= 3; int next_hash = ((E_mod_3 == 0) ? 256 : diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 4b28e480..4e71f8d2 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -33,7 +33,7 @@ load_vector_int(BitStream& bit_stream, int nitems, std::vector& vec, { vec.push_back(T()); } - vec[i].*field = bit_stream.getBits(bits_wanted); + vec.at(i).*field = bit_stream.getBits(bits_wanted); } if (static_cast(vec.size()) != nitems) { @@ -54,9 +54,9 @@ load_vector_vector(BitStream& bit_stream, // into the vec2 vector field of the ith item of vec1. for (int i1 = 0; i1 < nitems1; ++i1) { - for (int i2 = 0; i2 < vec1[i1].*nitems2; ++i2) + for (int i2 = 0; i2 < vec1.at(i1).*nitems2; ++i2) { - (vec1[i1].*vec2).push_back(bit_stream.getBits(bits_wanted)); + (vec1.at(i1).*vec2).push_back(bit_stream.getBits(bits_wanted)); } } bit_stream.skipToNextByte(); @@ -237,8 +237,8 @@ QPDF::readLinearizationData() } // H: hint table offset/length for primary and overflow hint tables - int H0_offset = H_items[0]; - int H0_length = H_items[1]; + int H0_offset = H_items.at(0); + int H0_length = H_items.at(1); int H1_offset = 0; int H1_length = 0; if (H_items.size() == 4) @@ -246,8 +246,8 @@ QPDF::readLinearizationData() // Acrobat doesn't read or write these (as PDF 1.4), so we // don't have a way to generate a test case. // QTC::TC("qpdf", "QPDF overflow hint table"); - H1_offset = H_items[2]; - H1_length = H_items[3]; + H1_offset = H_items.at(2); + H1_length = H_items.at(3); } // P: first page number @@ -470,7 +470,7 @@ QPDF::readHSharedObject(BitStream h) 1, &HSharedObjectEntry::signature_present); for (int i = 0; i < nitems; ++i) { - if (entries[i].signature_present) + if (entries.at(i).signature_present) { // Skip 128-bit MD5 hash. These are not supported by // acrobat, so they should probably never be there. We @@ -512,7 +512,7 @@ QPDF::checkLinearizationInternal() // O: object number of first page std::vector const& pages = getAllPages(); - if (p.first_page_object != pages[0].getObjectID()) + if (p.first_page_object != pages.at(0).getObjectID()) { QTC::TC("qpdf", "QPDF err /O mismatch"); errors.push_back("first page object (/O) mismatch"); @@ -528,7 +528,7 @@ QPDF::checkLinearizationInternal() for (int i = 0; i < npages; ++i) { - QPDFObjectHandle const& page = pages[i]; + QPDFObjectHandle const& page = pages.at(i); QPDFObjGen og(page.getObjGen()); if (this->xref_table[og].getType() == 2) { @@ -776,7 +776,7 @@ QPDF::checkHPageOffset(std::list& errors, unsigned int npages = pages.size(); int table_offset = adjusted_offset( this->page_offset_hints.first_page_offset); - QPDFObjGen first_page_og(pages[0].getObjGen()); + QPDFObjGen first_page_og(pages.at(0).getObjGen()); assert(this->xref_table.count(first_page_og) > 0); int offset = getLinearizationOffset(first_page_og); if (table_offset != offset) @@ -786,13 +786,13 @@ QPDF::checkHPageOffset(std::list& errors, for (unsigned int pageno = 0; pageno < npages; ++pageno) { - QPDFObjGen page_og(pages[pageno].getObjGen()); + QPDFObjGen page_og(pages.at(pageno).getObjGen()); int first_object = page_og.getObj(); assert(this->xref_table.count(page_og) > 0); offset = getLinearizationOffset(page_og); - HPageOffsetEntry& he = this->page_offset_hints.entries[pageno]; - CHPageOffsetEntry& ce = this->c_page_offset_data.entries[pageno]; + HPageOffsetEntry& he = this->page_offset_hints.entries.at(pageno); + CHPageOffsetEntry& ce = this->c_page_offset_data.entries.at(pageno); int h_nobjects = he.delta_nobjects + this->page_offset_hints.min_nobjects; if (h_nobjects != ce.nobjects) @@ -837,7 +837,7 @@ QPDF::checkHPageOffset(std::list& errors, for (int i = 0; i < he.nshared_objects; ++i) { - int idx = he.shared_identifiers[i]; + int idx = he.shared_identifiers.at(i); if (shared_idx_to_obj.count(idx) == 0) { throw std::logic_error( @@ -849,13 +849,13 @@ QPDF::checkHPageOffset(std::list& errors, for (int i = 0; i < ce.nshared_objects; ++i) { - int idx = ce.shared_identifiers[i]; + int idx = ce.shared_identifiers.at(i); if (idx >= this->c_shared_object_data.nshared_total) { throw std::logic_error( "index out of bounds for shared object hint table"); } - int obj = this->c_shared_object_data.entries[idx].object; + int obj = this->c_shared_object_data.entries.at(idx).object; computed_shared.insert(obj); } @@ -923,7 +923,7 @@ QPDF::checkHSharedObject(std::list& errors, // The first nshared_first_page objects are consecutive // objects starting with the first page object. The rest are // consecutive starting from the first_shared_obj object. - int cur_object = pages[0].getObjectID(); + int cur_object = pages.at(0).getObjectID(); for (int i = 0; i < so.nshared_total; ++i) { if (i == so.nshared_first_page) @@ -937,7 +937,7 @@ QPDF::checkHSharedObject(std::list& errors, } else { - int obj = this->part8[0].getObjectID(); + int obj = this->part8.at(0).getObjectID(); if (obj != so.first_shared_obj) { errors.push_back( @@ -965,7 +965,7 @@ QPDF::checkHSharedObject(std::list& errors, } idx_to_obj[i] = cur_object; - HSharedObjectEntry& se = so.entries[i]; + HSharedObjectEntry& se = so.entries.at(i); int nobjects = se.nobjects_minus_one + 1; int length = lengthNextN(cur_object, nobjects, errors); int h_length = so.min_group_length + se.delta_group_length; @@ -1146,7 +1146,7 @@ QPDF::dumpHPageOffset() for (int i1 = 0; i1 < this->linp.npages; ++i1) { - HPageOffsetEntry& pe = t.entries[i1]; + HPageOffsetEntry& pe = t.entries.at(i1); *out_stream << "Page " << i1 << ":" << std::endl << " nobjects: " << pe.delta_nobjects + t.min_nobjects @@ -1162,9 +1162,9 @@ QPDF::dumpHPageOffset() for (int i2 = 0; i2 < pe.nshared_objects; ++i2) { *out_stream << " identifier " << i2 << ": " - << pe.shared_identifiers[i2] << std::endl; + << pe.shared_identifiers.at(i2) << std::endl; *out_stream << " numerator " << i2 << ": " - << pe.shared_numerators[i2] << std::endl; + << pe.shared_numerators.at(i2) << std::endl; } } } @@ -1191,7 +1191,7 @@ QPDF::dumpHSharedObject() for (int i = 0; i < t.nshared_total; ++i) { - HSharedObjectEntry& se = t.entries[i]; + HSharedObjectEntry& se = t.entries.at(i); *out_stream << "Shared Object " << i << ":" << std::endl; *out_stream << " group length: " << se.delta_group_length + t.min_group_length << std::endl; @@ -1522,7 +1522,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) // will do the same. // First, place the actual first page object itself. - QPDFObjGen first_page_og(pages[0].getObjGen()); + QPDFObjGen first_page_og(pages.at(0).getObjGen()); if (! lc_first_page_private.count(first_page_og)) { throw std::logic_error( @@ -1530,8 +1530,8 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) "object not in lc_first_page_private"); } lc_first_page_private.erase(first_page_og); - this->c_linp.first_page_object = pages[0].getObjectID(); - this->part6.push_back(pages[0]); + this->c_linp.first_page_object = pages.at(0).getObjectID(); + this->part6.push_back(pages.at(0)); // The PDF spec "recommends" an order for the rest of the objects, // but we are going to disregard it except to the extent that it @@ -1562,7 +1562,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) // in garbage values for all the shared object identifiers on the // first page. - this->c_page_offset_data.entries[0].nobjects = this->part6.size(); + this->c_page_offset_data.entries.at(0).nobjects = this->part6.size(); // Part 7: other pages' private objects @@ -1571,7 +1571,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) { // Place this page's page object - QPDFObjGen page_og(pages[i].getObjGen()); + QPDFObjGen page_og(pages.at(i).getObjGen()); if (! lc_other_page_private.count(page_og)) { throw std::logic_error( @@ -1580,12 +1580,12 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) QUtil::int_to_string(i) + " not in lc_other_page_private"); } lc_other_page_private.erase(page_og); - this->part7.push_back(pages[i]); + this->part7.push_back(pages.at(i)); // Place all non-shared objects referenced by this page, // updating the page object count for the hint table. - this->c_page_offset_data.entries[i].nobjects = 1; + this->c_page_offset_data.entries.at(i).nobjects = 1; ObjUser ou(ObjUser::ou_page, i); assert(this->obj_user_to_objects.count(ou) > 0); @@ -1598,7 +1598,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) { lc_other_page_private.erase(og); this->part7.push_back(objGenToIndirect(og)); - ++this->c_page_offset_data.entries[i].nobjects; + ++this->c_page_offset_data.entries.at(i).nobjects; } } } @@ -1649,7 +1649,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) // thumbnail hint tables. for (unsigned int i = 0; i < npages; ++i) { - QPDFObjectHandle thumb = pages[i].getKey("/Thumb"); + QPDFObjectHandle thumb = pages.at(i).getKey("/Thumb"); thumb = getUncompressedObject(thumb, object_stream_data); if (! thumb.isNull()) { @@ -1758,7 +1758,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) if (! this->part8.empty()) { this->c_shared_object_data.first_shared_obj = - this->part8[0].getObjectID(); + this->part8.at(0).getObjectID(); for (std::vector::iterator iter = this->part8.begin(); iter != this->part8.end(); ++iter) @@ -1781,7 +1781,7 @@ QPDF::calculateLinearizationData(std::map const& object_stream_data) for (unsigned int i = 1; i < npages; ++i) { - CHPageOffsetEntry& pe = this->c_page_offset_data.entries[i]; + CHPageOffsetEntry& pe = this->c_page_offset_data.entries.at(i); ObjUser ou(ObjUser::ou_page, i); assert(this->obj_user_to_objects.count(ou) > 0); std::set const& ogs = this->obj_user_to_objects[ou]; @@ -1892,12 +1892,12 @@ QPDF::calculateHPageOffset( // Calculate minimum and maximum values for number of objects per // page and page length. - int min_nobjects = cphe[0].nobjects; + int min_nobjects = cphe.at(0).nobjects; int max_nobjects = min_nobjects; int min_length = outputLengthNextN( - pages[0].getObjectID(), min_nobjects, lengths, obj_renumber); + pages.at(0).getObjectID(), min_nobjects, lengths, obj_renumber); int max_length = min_length; - int max_shared = cphe[0].nshared_objects; + int max_shared = cphe.at(0).nshared_objects; HPageOffset& ph = this->page_offset_hints; std::vector& phe = ph.entries; @@ -1912,10 +1912,10 @@ QPDF::calculateHPageOffset( // Repeat calculations for page 0 so we can assign to phe[i] // without duplicating those assignments. - int nobjects = cphe[i].nobjects; + int nobjects = cphe.at(i).nobjects; int length = outputLengthNextN( - pages[i].getObjectID(), nobjects, lengths, obj_renumber); - int nshared = cphe[i].nshared_objects; + pages.at(i).getObjectID(), nobjects, lengths, obj_renumber); + int nshared = cphe.at(i).nshared_objects; min_nobjects = std::min(min_nobjects, nobjects); max_nobjects = std::max(max_nobjects, nobjects); @@ -1923,13 +1923,13 @@ QPDF::calculateHPageOffset( max_length = std::max(max_length, length); max_shared = std::max(max_shared, nshared); - phe[i].delta_nobjects = nobjects; - phe[i].delta_page_length = length; - phe[i].nshared_objects = nshared; + phe.at(i).delta_nobjects = nobjects; + phe.at(i).delta_page_length = length; + phe.at(i).nshared_objects = nshared; } ph.min_nobjects = min_nobjects; - int in_page0_id = pages[0].getObjectID(); + int in_page0_id = pages.at(0).getObjectID(); int out_page0_id = (*(obj_renumber.find(in_page0_id))).second; ph.first_page_offset = (*(xref.find(out_page0_id))).second.getOffset(); ph.nbits_delta_nobjects = nbits(max_nobjects - min_nobjects); @@ -1951,17 +1951,17 @@ QPDF::calculateHPageOffset( for (unsigned int i = 0; i < npages; ++i) { // Adjust delta entries - assert(phe[i].delta_nobjects >= min_nobjects); - assert(phe[i].delta_page_length >= min_length); - phe[i].delta_nobjects -= min_nobjects; - phe[i].delta_page_length -= min_length; - phe[i].delta_content_length = phe[i].delta_page_length; + assert(phe.at(i).delta_nobjects >= min_nobjects); + assert(phe.at(i).delta_page_length >= min_length); + phe.at(i).delta_nobjects -= min_nobjects; + phe.at(i).delta_page_length -= min_length; + phe.at(i).delta_content_length = phe.at(i).delta_page_length; - for (int j = 0; j < cphe[i].nshared_objects; ++j) + for (int j = 0; j < cphe.at(i).nshared_objects; ++j) { - phe[i].shared_identifiers.push_back( - cphe[i].shared_identifiers[j]); - phe[i].shared_numerators.push_back(0); + phe.at(i).shared_identifiers.push_back( + cphe.at(i).shared_identifiers.at(j)); + phe.at(i).shared_numerators.push_back(0); } } } @@ -1979,18 +1979,18 @@ QPDF::calculateHSharedObject( soe.clear(); int min_length = outputLengthNextN( - csoe[0].object, 1, lengths, obj_renumber); + csoe.at(0).object, 1, lengths, obj_renumber); int max_length = min_length; for (int i = 0; i < cso.nshared_total; ++i) { // Assign absolute numbers to deltas; adjust later int length = outputLengthNextN( - csoe[i].object, 1, lengths, obj_renumber); + csoe.at(i).object, 1, lengths, obj_renumber); min_length = std::min(min_length, length); max_length = std::max(max_length, length); soe.push_back(HSharedObjectEntry()); - soe[i].delta_group_length = length; + soe.at(i).delta_group_length = length; } if (soe.size() != static_cast(cso.nshared_total)) { @@ -2012,8 +2012,8 @@ QPDF::calculateHSharedObject( for (int i = 0; i < cso.nshared_total; ++i) { // Adjust deltas - assert(soe[i].delta_group_length >= min_length); - soe[i].delta_group_length -= min_length; + assert(soe.at(i).delta_group_length >= min_length); + soe.at(i).delta_group_length -= min_length; } } @@ -2051,7 +2051,7 @@ write_vector_int(BitWriter& w, int nitems, std::vector& vec, for (int i = 0; i < nitems; ++i) { - w.writeBits(vec[i].*field, bits); + w.writeBits(vec.at(i).*field, bits); } // The PDF spec says that each hint table starts at a byte // boundary. Each "row" actually must start on a byte boundary. @@ -2068,9 +2068,9 @@ write_vector_vector(BitWriter& w, // from the vec2 vector field of the ith item of vec1. for (int i1 = 0; i1 < nitems1; ++i1) { - for (int i2 = 0; i2 < vec1[i1].*nitems2; ++i2) + for (int i2 = 0; i2 < vec1.at(i1).*nitems2; ++i2) { - w.writeBits((vec1[i1].*vec2)[i2], bits); + w.writeBits((vec1.at(i1).*vec2).at(i2), bits); } } w.flush(); @@ -2151,7 +2151,7 @@ QPDF::writeHSharedObject(BitWriter& w) for (int i = 0; i < nitems; ++i) { // If signature were present, we'd have to write a 128-bit hash. - assert(entries[i].signature_present == 0); + assert(entries.at(i).signature_present == 0); } write_vector_int(w, nitems, entries, t.nbits_nobjects, diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 35663c45..5299d90f 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -91,7 +91,7 @@ QPDF::optimize(std::map const& object_stream_data, for (int pageno = 0; pageno < n; ++pageno) { updateObjectMaps(ObjUser(ObjUser::ou_page, pageno), - this->all_pages[pageno]); + this->all_pages.at(pageno)); } // Traverse document-level items diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index b88ed5fa..e8d107b3 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -114,8 +114,8 @@ QPDF::flattenPagesTree() for (int pos = 0; pos < len; ++pos) { // populate pageobj_to_pages_pos and fix parent pointer - insertPageobjToPage(this->all_pages[pos], pos, true); - this->all_pages[pos].replaceKey("/Parent", pages); + insertPageobjToPage(this->all_pages.at(pos), pos, true); + this->all_pages.at(pos).replaceKey("/Parent", pages); } pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages)); @@ -194,7 +194,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos) assert(this->all_pages.size() == static_cast(npages)); for (int i = pos + 1; i < npages; ++i) { - insertPageobjToPage(this->all_pages[i], i, false); + insertPageobjToPage(this->all_pages.at(i), i, false); } insertPageobjToPage(newpage, pos, true); assert(this->pageobj_to_pages_pos.size() == static_cast(npages)); @@ -221,7 +221,7 @@ QPDF::removePage(QPDFObjectHandle page) assert(this->pageobj_to_pages_pos.size() == static_cast(npages)); for (int i = pos; i < npages; ++i) { - insertPageobjToPage(this->all_pages[i], i, false); + insertPageobjToPage(this->all_pages.at(i), i, false); } } diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index de3099d2..cf455061 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -201,7 +201,7 @@ QUtil::hex_encode(std::string const& input) for (unsigned int i = 0; i < input.length(); ++i) { result += QUtil::int_to_string_base( - static_cast(static_cast(input[i])), 16, 2); + static_cast(static_cast(input.at(i))), 16, 2); } return result; } diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 876b150f..ed648129 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -441,7 +441,7 @@ static std::vector parse_numrange(char const* range, int max, p = 0; for (size_t i = 0; i < work.size(); i += 2) { - int num = work[i]; + int num = work.at(i); // max == 0 means we don't know the max and are just // testing for valid syntax. if ((max > 0) && ((num < 1) || (num > max))) @@ -451,11 +451,11 @@ static std::vector parse_numrange(char const* range, int max, } if (i == 0) { - result.push_back(work[i]); + result.push_back(work.at(i)); } else { - int separator = work[i-1]; + int separator = work.at(i-1); if (separator == comma) { result.push_back(num); @@ -1664,7 +1664,7 @@ int main(int argc, char* argv[]) // Pages are specified from 1 but numbered // from 0 in the vector int pageno = *pageno_iter - 1; - pdf.addPage(page_data.orig_pages[pageno], false); + pdf.addPage(page_data.orig_pages.at(pageno), false); if (page_data.qpdf == &pdf) { // This is a page from the original file. @@ -1683,7 +1683,7 @@ int main(int argc, char* argv[]) { if (selected_from_orig.count(pageno) == 0) { - pdf.replaceObject(orig_pages[pageno].getObjGen(), + pdf.replaceObject(orig_pages.at(pageno).getObjGen(), QPDFObjectHandle::newNull()); } } diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 31fc0525..49dfbc64 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -603,10 +603,10 @@ void runtest(int n, char const* filename1, char const* arg2) else if (n == 10) { std::vector pages = pdf.getAllPages(); - pages[0].addPageContents( + pages.at(0).addPageContents( QPDFObjectHandle::newStream( &pdf, "BT /F1 12 Tf 72 620 Td (Baked) Tj ET\n"), true); - pages[0].addPageContents( + pages.at(0).addPageContents( QPDFObjectHandle::newStream( &pdf, "BT /F1 18 Tf 72 520 Td (Mashed) Tj ET\n"), false); @@ -659,7 +659,7 @@ void runtest(int n, char const* filename1, char const* arg2) " not called 4-page file"); } // Swap pages 2 and 3 - pdf.swapObjects(pages[1].getObjGen(), pages[2].getObjGen()); + pdf.swapObjects(pages.at(1).getObjGen(), pages.at(2).getObjGen()); // Replace object and swap objects QPDFObjectHandle trailer = pdf.getTrailer(); QPDFObjectHandle qdict = trailer.getKey("/QDict"); @@ -700,7 +700,7 @@ void runtest(int n, char const* filename1, char const* arg2) std::map dict_items = qarray.getDictAsMap(); if ((array_elements.size() == 1) && - (array_elements[0].getName() == "/Array") && + (array_elements.at(0).getName() == "/Array") && (dict_items.size() == 1) && (dict_items["/NewDict"].getIntValue() == 2)) { @@ -738,12 +738,12 @@ void runtest(int n, char const* filename1, char const* arg2) assert(pages.size() == 9); pdf.removePage(*pages.begin()); // original page 0 assert(pages.size() == 8); - checkPageContents(pages[4], "Original page 5"); - pdf.removePage(pages[4]); // original page 5 + checkPageContents(pages.at(4), "Original page 5"); + pdf.removePage(pages.at(4)); // original page 5 assert(pages.size() == 7); - checkPageContents(pages[4], "Original page 6"); - checkPageContents(pages[0], "Original page 1"); - checkPageContents(pages[6], "Original page 8"); + checkPageContents(pages.at(4), "Original page 6"); + checkPageContents(pages.at(0), "Original page 1"); + checkPageContents(pages.at(6), "Original page 8"); // Insert pages @@ -760,7 +760,7 @@ void runtest(int n, char const* filename1, char const* arg2) // dictionary and modify it. Using the results of // getDictAsMap to create a new dictionary effectively creates // a shallow copy. - QPDFObjectHandle page_template = pages[0]; + QPDFObjectHandle page_template = pages.at(0); std::vector new_pages; for (std::vector::iterator iter = contents.begin(); iter != contents.end(); ++iter) @@ -781,25 +781,25 @@ void runtest(int n, char const* filename1, char const* arg2) } // Now insert the pages - pdf.addPage(new_pages[0], true); - checkPageContents(pages[0], "New page 1"); - pdf.addPageAt(new_pages[1], true, pages[0]); - assert(pages[0].getObjGen() == new_pages[1].getObjGen()); - pdf.addPageAt(new_pages[2], true, pages[5]); - assert(pages[5].getObjGen() == new_pages[2].getObjGen()); - pdf.addPageAt(new_pages[3], false, pages[5]); - assert(pages[6].getObjGen() == new_pages[3].getObjGen()); + pdf.addPage(new_pages.at(0), true); + checkPageContents(pages.at(0), "New page 1"); + pdf.addPageAt(new_pages.at(1), true, pages.at(0)); + assert(pages.at(0).getObjGen() == new_pages.at(1).getObjGen()); + pdf.addPageAt(new_pages.at(2), true, pages.at(5)); + assert(pages.at(5).getObjGen() == new_pages.at(2).getObjGen()); + pdf.addPageAt(new_pages.at(3), false, pages.at(5)); + assert(pages.at(6).getObjGen() == new_pages.at(3).getObjGen()); assert(pages.size() == 11); - pdf.addPage(new_pages[4], false); - assert(pages[11].getObjGen() == new_pages[4].getObjGen()); - pdf.addPageAt(new_pages[5], false, pages.back()); + pdf.addPage(new_pages.at(4), false); + assert(pages.at(11).getObjGen() == new_pages.at(4).getObjGen()); + pdf.addPageAt(new_pages.at(5), false, pages.back()); assert(pages.size() == 13); - checkPageContents(pages[0], "New page 0"); - checkPageContents(pages[1], "New page 1"); - checkPageContents(pages[5], "New page 5"); - checkPageContents(pages[6], "New page 6"); - checkPageContents(pages[11], "New page 11"); - checkPageContents(pages[12], "New page 12"); + checkPageContents(pages.at(0), "New page 0"); + checkPageContents(pages.at(1), "New page 1"); + checkPageContents(pages.at(5), "New page 5"); + checkPageContents(pages.at(6), "New page 6"); + checkPageContents(pages.at(11), "New page 11"); + checkPageContents(pages.at(12), "New page 12"); // Exercise writing to FILE* FILE* out = QUtil::safe_fopen("a.pdf", "wb"); @@ -816,7 +816,7 @@ void runtest(int n, char const* filename1, char const* arg2) QPDFObjectHandle contents = createPageContents(pdf, "New page 10"); QPDFObjectHandle page = pdf.makeIndirectObject( - QPDFObjectHandle(all_pages[0]).shallowCopy()); + QPDFObjectHandle(all_pages.at(0)).shallowCopy()); page.replaceKey("/Contents", contents); // Insert the page manually. @@ -843,7 +843,7 @@ void runtest(int n, char const* filename1, char const* arg2) // The input file to this test case is broken to exercise an // error condition. std::vector const& pages = pdf.getAllPages(); - pdf.removePage(pages[0]); + pdf.removePage(pages.at(0)); std::cout << "you can't see this" << std::endl; } else if (n == 18) @@ -854,7 +854,7 @@ void runtest(int n, char const* filename1, char const* arg2) // Remove pages from various places, checking to make sure // that our pages reference is getting updated. assert(pages.size() == 10); - QPDFObjectHandle page5 = pages[5]; + QPDFObjectHandle page5 = pages.at(5); pdf.removePage(page5); pdf.addPage(page5, false); assert(pages.size() == 10); @@ -871,7 +871,7 @@ void runtest(int n, char const* filename1, char const* arg2) std::vector const& pages = pdf.getAllPages(); // Try to insert a page that's already there. - pdf.addPage(pages[5], false); + pdf.addPage(pages.at(5), false); std::cout << "you can't see this" << std::endl; } else if (n == 20) @@ -893,7 +893,7 @@ void runtest(int n, char const* filename1, char const* arg2) { // Try to shallow copy a stream std::vector const& pages = pdf.getAllPages(); - QPDFObjectHandle page = pages[0]; + QPDFObjectHandle page = pages.at(0); QPDFObjectHandle contents = page.getKey("/Contents"); contents.shallowCopy(); std::cout << "you can't see this" << std::endl; @@ -902,7 +902,7 @@ void runtest(int n, char const* filename1, char const* arg2) { // Try to remove a page we don't have std::vector const& pages = pdf.getAllPages(); - QPDFObjectHandle page = pages[0]; + QPDFObjectHandle page = pages.at(0); pdf.removePage(page); pdf.removePage(page); std::cout << "you can't see this" << std::endl; @@ -1109,9 +1109,9 @@ void runtest(int n, char const* filename1, char const* arg2) QPDF final; final.processFile("b.pdf", "user"); std::vector pages = pdf.getAllPages(); - std::string orig_contents = getPageContents(pages[0]); + std::string orig_contents = getPageContents(pages.at(0)); pages = final.getAllPages(); - std::string new_contents = getPageContents(pages[0]); + std::string new_contents = getPageContents(pages.at(0)); if (orig_contents != new_contents) { std::cout << "oops -- page contents don't match" << std::endl @@ -1226,7 +1226,7 @@ void runtest(int n, char const* filename1, char const* arg2) bool is_binary = false; for (size_t i = 0; i < data.size(); ++i) { - if ((data[i] < 0) || (data[i] > 126)) + if ((data.at(i) < 0) || (data.at(i) > 126)) { is_binary = true; break; @@ -1239,9 +1239,9 @@ void runtest(int n, char const* filename1, char const* arg2) i < std::min(data.size(), static_cast(20)); ++i) { - if ((data[i] >= 32) && (data[i] <= 126)) + if ((data.at(i) >= 32) && (data.at(i) <= 126)) { - t += data[i]; + t += data.at(i); } else { diff --git a/qpdf/test_large_file.cc b/qpdf/test_large_file.cc index 7ba5f6cb..a7ed7170 100644 --- a/qpdf/test_large_file.cc +++ b/qpdf/test_large_file.cc @@ -286,8 +286,8 @@ static void check_pdf(char const* filename) { int pageno = i + 1; std::cout << "page " << pageno << " of " << npages << std::endl; - check_page_contents(pageno, pages[i]); - check_image(pageno, pages[i]); + check_page_contents(pageno, pages.at(i)); + check_image(pageno, pages.at(i)); } }