0

[PDF Ink Signatures] Add pdf_ink_reader_fuzzer to fuzz libtess2 code

Add a new fuzzer using FUZZ_TEST to indirectly fuzz libtess2 code. Since
the production code in pdfium_ink_reader.cc executes libtess2 code by
calling ink::CreateMeshFromPolyline(), try to fuzz that. Except the code
has some trouble with extreme float point values. To avoid that, add a
CreateInkMeshFromPolyline() wrapper, which rejects extreme values, and
use that inside of pdfium_ink_reader.cc. Then expose it in
pdfium_ink_reader.h as CreateInkMeshFromPolylineForTesting(), and fuzz
that.

Bug: 339682315
Low-Coverage-Reason: COVERAGE_UNDERREPORTED Fuzzer not being counted?
Change-Id: Ic9b2327f56e07d249901d4f587d8c881eb01fe60
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5992141
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Adrian Taylor <adetaylor@google.com>
Reviewed-by: Paul Semel <paulsemel@chromium.org>
Reviewed-by: Alan Screen <awscreen@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1379811}
This commit is contained in:
Lei Zhang
2024-11-07 18:26:07 +00:00
committed by Chromium LUCI CQ
parent c1eeb671c5
commit b8162a9b38
6 changed files with 92 additions and 0 deletions

@ -554,6 +554,18 @@ if (enable_pdf) {
"//base",
]
}
if (enable_pdf_ink2) {
test("pdf_ink_reader_fuzzer") {
fuzztests = [ "PdfInkReaderFuzzer.CreateMeshFromPolylineDoesntCrash" ]
sources = [ "pdfium/pdfium_ink_reader_fuzzer.cc" ]
deps = [
":internal",
"//third_party/fuzztest:fuzztest_gtest_main",
"//third_party/ink",
]
}
}
} else {
# Dummy groups when PDF support is disabled so targets can unconditionally
# depend on it.

@ -12,6 +12,12 @@ namespace chrome_pdf {
// Signature for "V2" PDF page objects. Do not change.
inline constexpr char kInkAnnotationIdentifierKeyV2[] = "GOOG:INKIsInker";
// Since PDFium does not support UserUnit, this is the maximum possible PDF
// dimension.
// TODO(crbug.com/42271703): If the bug gets fixed, update the code that assumes
// this is the limit.
inline constexpr int kMaxPdfDimensionInches = 200;
} // namespace chrome_pdf
#endif // PDF_PDF_INK_CONSTANTS_H_

@ -6,3 +6,9 @@ include_rules = [
"+ui/accessibility",
"+ui/gfx/codec",
]
specific_include_rules = {
".*_fuzzer\.cc": [
"+third_party/fuzztest",
],
}

@ -15,6 +15,7 @@
#include "pdf/pdf_ink_conversions.h"
#include "pdf/pdf_ink_transform.h"
#include "pdf/pdfium/pdfium_api_wrappers.h"
#include "printing/units.h"
#include "third_party/ink/src/ink/geometry/mesh.h"
#include "third_party/ink/src/ink/geometry/modeled_shape.h"
#include "third_party/ink/src/ink/geometry/point.h"
@ -63,6 +64,30 @@ ink::Point GetTransformedInkPoint(const gfx::AxisTransform2d& transform,
return InkPointFromGfxPoint(transform.MapPoint(point));
}
// Wrapper around ink::CreateMeshFromPolyline() to convert `polyline` into an
// ink::Mesh. It applies an additional check to make sure the points in
// `polyline` have sane values.
std::optional<ink::Mesh> CreateInkMeshFromPolyline(
const std::vector<ink::Point>& polyline) {
// Limit for ink::Point values in pixels. It is divided by 2 because the limit
// extends half way in the negative range.
constexpr int kInkPointDimensionLimit =
kMaxPdfDimensionInches * printing::kPixelsPerInch / 2;
for (const auto& pt : polyline) {
if (pt.x < -kInkPointDimensionLimit || pt.x > kInkPointDimensionLimit ||
pt.y < -kInkPointDimensionLimit || pt.y > kInkPointDimensionLimit) {
return std::nullopt;
}
}
auto mesh = ink::CreateMeshFromPolyline(polyline);
if (!mesh.ok()) {
return std::nullopt;
}
return *mesh;
}
// Creates an ink::Mesh from `polyline`. If it is valid, append it to `meshes`.
void AppendPolylineToMeshesList(std::vector<ink::Mesh>& meshes,
const std::vector<ink::Point>& polyline) {
@ -170,4 +195,9 @@ std::vector<ReadV2InkPathResult> ReadV2InkPathsFromPageAsModeledShapes(
return results;
}
std::optional<ink::Mesh> CreateInkMeshFromPolylineForTesting( // IN-TEST
const std::vector<ink::Point>& polyline) {
return CreateInkMeshFromPolyline(polyline);
}
} // namespace chrome_pdf

@ -5,10 +5,13 @@
#ifndef PDF_PDFIUM_PDFIUM_INK_READER_H_
#define PDF_PDFIUM_PDFIUM_INK_READER_H_
#include <optional>
#include <vector>
#include "pdf/buildflags.h"
#include "third_party/ink/src/ink/geometry/mesh.h"
#include "third_party/ink/src/ink/geometry/modeled_shape.h"
#include "third_party/ink/src/ink/geometry/point.h"
#include "third_party/pdfium/public/fpdfview.h"
static_assert(BUILDFLAG(ENABLE_PDF_INK2), "ENABLE_PDF_INK2 not set to true");
@ -32,6 +35,10 @@ struct ReadV2InkPathResult {
std::vector<ReadV2InkPathResult> ReadV2InkPathsFromPageAsModeledShapes(
FPDF_PAGE page);
// Exposes internal CreateInkMeshFromPolyline() for testing.
std::optional<ink::Mesh> CreateInkMeshFromPolylineForTesting(
const std::vector<ink::Point>& polyline);
} // namespace chrome_pdf
#endif // PDF_PDFIUM_PDFIUM_INK_READER_H_

@ -0,0 +1,31 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/pdfium/pdfium_ink_reader.h"
#include <vector>
#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
#include "third_party/ink/src/ink/geometry/point.h"
namespace chrome_pdf {
namespace {
fuzztest::Domain<ink::Point> FiniteInkPoint() {
return fuzztest::StructOf<ink::Point>(fuzztest::Finite<float>(),
fuzztest::Finite<float>());
}
void CreateMeshFromPolylineDoesntCrash(
const std::vector<ink::Point>& polyline) {
auto mesh = CreateInkMeshFromPolylineForTesting(polyline);
}
} // namespace
FUZZ_TEST(PdfInkReaderFuzzer, CreateMeshFromPolylineDoesntCrash)
.WithDomains(fuzztest::VectorOf(FiniteInkPoint()));
} // namespace chrome_pdf