Class ResourceLimits
Caps that protect a conversion against hostile or degenerate input (zip bombs, runaway page counts, excessive image payloads, pathological nesting). A null limit means "unlimited". Exceeding a limit raises ResourceLimitExceededException regardless of strict mode.
public sealed class ResourceLimits
- Inheritance
-
ResourceLimits
- Inherited Members
Remarks
Most limits are on by default, and the ones that are not are the exception. An earlier revision of this type defaulted almost everything to unlimited on the reasoning that "the worst case is bounded by input size". That reasoning is wrong wherever the input is compressed or is walked recursively, which is most of a document format: a kilobyte of deflate reaches a gigabyte, and a kilobyte of nested markup reaches the end of the stack. Any limit whose worst case is not proportional to the bytes supplied now ships with a default chosen to sit orders of magnitude above real documents and orders of magnitude below the point where a machine is in trouble.
The limits still defaulting to null are the ones where the worst case genuinely is proportional to the input and where any value would be a policy guess about the caller's workload rather than a safety property: MaxInputBytes, MaxOutputBytes, MaxPages, MaxApproximateMemoryBytes and MaxWallClockTime. A host that converts untrusted documents should set those to whatever its own budget is.
Constructors
Fields
- DefaultMaxImagePixels
The default cap on MaxImagePixels: 256 megapixels, which is a 16000×16000 image. A 300 DPI A4 scan is 8.7 MP and a large photograph is under 60, so this leaves two orders of magnitude of head room while bounding one decode to about a gigabyte.
- DefaultMaxKeyDerivationSpinCount
The default cap on MaxKeyDerivationSpinCount: one million iterations. Files Word produces spin 100,000 times, so this leaves an order of magnitude of head room while keeping the worst case to about a second of hashing.
- DefaultMaxNestedTableDepth
The default cap on MaxNestedTableDepth: 64 levels. Layout already stops laying out nested content past 16 and says so, so nothing between 16 and 64 renders meaningfully — the value is set well above the point of diminishing returns purely so that no real document meets it.
- DefaultMaxPackageCompressionRatio
The default cap on MaxPackageCompressionRatio: 200:1 across the package. WordprocessingML XML compresses at roughly 10:1 and rarely passes 20:1, while the classic zip bomb starts above 1000:1.
- DefaultMaxPackageEntryCount
The default cap on MaxPackageEntryCount: 10,000 parts. A document with a thousand images has about 2,000; the value exists to bound the table, which is allocated from a count the package declares.
- DefaultMaxPdfContentOperations
The default cap on MaxPdfContentOperations: twenty million operators.
- DefaultMaxPdfDecompressedBytes
The default cap on MaxPdfDecompressedBytes: 256 MiB per stream.
- DefaultMaxPdfFunctionSteps
The default cap on MaxPdfFunctionSteps: 100,000 steps per evaluation.
- DefaultMaxPdfNestingDepth
The default cap on MaxPdfNestingDepth: 32 levels.
- DefaultMaxPdfObjectCount
The default cap on MaxPdfObjectCount: five million indirect objects.
- DefaultMaxReportDataSetRows
The default cap on MaxReportDataSetRows: two million rows per dataset.
- DefaultMaxReportElementCount
The default cap on MaxReportElementCount: two million elements.
- DefaultMaxReportExpressionAllocation
The default cap on MaxReportExpressionAllocation: sixteen million units per evaluation.
- DefaultMaxReportExpressionDepth
The default cap on MaxReportExpressionDepth: 256 levels.
- DefaultMaxReportExpressionSteps
The default cap on MaxReportExpressionSteps: ten million steps per evaluation.
- DefaultMaxReportGroupInstances
The default cap on MaxReportGroupInstances: four million group instances per report run.
- DefaultMaxReportInstanceNodes
The default cap on MaxReportInstanceNodes: ten million instance nodes per report run.
- DefaultMaxReportNestingDepth
The default cap on MaxReportNestingDepth: 256 levels.
- DefaultMaxReportPages
The default cap on MaxReportPages: one hundred thousand pages per pagination.
- DefaultMaxReportSubreportDepth
The default cap on MaxReportSubreportDepth: sixteen subreports inside one another.
- DefaultMaxUncompressedPartBytes
The default cap on MaxUncompressedPartBytes: 512 MiB per package part. The largest part a real document carries is its media or its main story, both far below this; a zip bomb passes it in its first entry.
Properties
- MaxApproximateMemoryBytes
Gets or sets an approximate per-conversion working-memory budget used by internal accounted buffers, or null for unlimited.
- MaxImagePixels
Gets or sets the maximum pixel count for any decoded image payload (
width * height), or null for unlimited.On by default (DefaultMaxImagePixels). Dimensions come out of a header a few bytes long; the buffer they size does not.
- MaxInputBytes
Gets or sets the maximum size of the input document in bytes, or null for unlimited.
- MaxKeyDerivationSpinCount
Gets or sets the largest key-derivation iteration count ("spin count") DocWright will honour from an encrypted document, or null for unlimited.
This is the one limit that is on by default (DefaultMaxKeyDerivationSpinCount), because the value it caps is chosen by whoever wrote the file. ECMA-376 agile encryption stores its spin count in the plaintext
EncryptionInfodescriptor, so a hostile document can simply ask for ten million rounds of SHA-512 and be honoured — a denial of service that needs no exploit, only a text editor. Every other limit here defaults to unlimited because its worst case is bounded by input size; this one is not.A document declaring more than the cap raises ResourceLimitExceededException before any hashing is done, so the cost of rejecting it does not depend on the value it asked for.
- MaxNestedTableDepth
Gets or sets the maximum nested table depth admitted during style resolution and layout, or null for unlimited.
On by default (DefaultMaxNestedTableDepth), and enforced by the resolver first: resolution is the first pass to walk the nested structure, so a document deep enough to exhaust the stack reaches it before layout ever runs. The layout engine applies the same value afterwards as a backstop.
- MaxOutputBytes
Gets or sets the maximum number of bytes a writer may emit, or null for unlimited.
The read and layout paths were already capped; writing needs the same treatment, because a pathological or hostile document model can produce output far larger than the input that created it. Exceeding the cap raises ResourceLimitExceededException rather than filling the destination.
- MaxPackageCompressionRatio
Gets or sets the maximum allowed package compression ratio (
totalUncompressedBytes / totalCompressedBytes), or null for unlimited.On by default (DefaultMaxPackageCompressionRatio). It is the cheapest of the package guards — the sizes come from the central directory, so nothing is inflated to compute it — and it rejects the ordinary bomb before a byte is read.
On the declared sizes being attacker-controlled. They are, and it does not help the attacker: the ZIP reader bounds each entry's decompressed stream by the size its metadata declares, so under-reporting a part to make the ratio look innocent also truncates that part on read. The result is a document that fails to parse, not one that expands. Over-reporting only makes the ratio worse. This is asserted rather than assumed — see the package-limit tests, which run on both target frameworks because the two use different ZIP implementations.
MaxUncompressedPartBytes remains the guard that bounds a single part regardless, and is the one that holds when a package spreads its expansion across entries that are each individually plausible.
- MaxPackageEntryCount
Gets or sets the maximum number of ZIP entries allowed in a package, or null for unlimited.
On by default (DefaultMaxPackageEntryCount).
- MaxPages
Gets or sets the maximum number of pages a conversion may lay out, or null for unlimited.
- MaxPdfContentOperations
Gets or sets the maximum number of content-stream operators PDF import will execute for one page, including operators reached through nested form XObjects and patterns, or null for unlimited.
On by default (DefaultMaxPdfContentOperations). Nesting is already bounded by MaxPdfNestingDepth, but depth is not the same as work: a pattern that tiles a thousand times, each tile drawing a form that draws a thousand paths, stays four levels deep and still costs a billion operations.
- MaxPdfDecompressedBytes
Gets or sets the maximum number of bytes any single PDF stream may expand to once its filter chain has run, or null for unlimited.
On by default (DefaultMaxPdfDecompressedBytes). This is the PDF face of the zip bomb MaxUncompressedPartBytes guards in a package:
/FlateDecodereaches roughly 1000:1 on a run of zeros, and a filter chain multiplies, so a kilobyte of input can ask for a gigabyte of output.
- MaxPdfFunctionSteps
Gets or sets the maximum number of steps one evaluation of a PDF type 4 (PostScript calculator) function may take, or null for unlimited.
On by default (DefaultMaxPdfFunctionSteps). The language has no loop, so a single evaluation is bounded by the program's own size — but a
SeparationorDeviceNtint transform is evaluated once per sample, and deeply nestedifelseprocedures make each of those evaluations arbitrarily expensive relative to the handful of bytes that requested it.
- MaxPdfNestingDepth
Gets or sets how deeply PDF import may nest a construct that can contain itself — a form XObject, a tiling pattern, a Type 3 glyph procedure, or a parsed object's arrays and dictionaries.
Unlike its neighbours this one has no unlimited setting: null, and any value above the reader's own absolute ceiling, both resolve to that ceiling. The failure this guards is StackOverflowException, which no
catchcontains, so a caller who could lift the ceiling would be escaping the typed exception surface rather than widening it. Setting it therefore tightens the bound and never removes it.On by default (DefaultMaxPdfNestingDepth), and for the same reason as MaxKeyDerivationSpinCount: the worst case is not bounded by input size. A two-hundred-byte PDF whose form XObject draws itself recurses forever, so there is no size at which the input is safe. Thirty-two levels is far past anything a real generator emits — Word, LibreOffice and Illustrator all stay in single digits.
- MaxPdfObjectCount
Gets or sets the maximum number of indirect objects PDF import will admit from one document, or null for unlimited.
On by default (DefaultMaxPdfObjectCount). A cross-reference stream or an object stream declares its entry count in a header, so a few bytes can ask the reader to allocate a table with billions of slots before a single object is read.
- MaxReportDataSetRows
Gets or sets the maximum number of rows one dataset may bring into a report run, or null for unlimited.
On by default (DefaultMaxReportDataSetRows). Rows come from the host's data provider, but a report run materializes every row it groups, sorts or totals, and a reader that never ends would otherwise run until memory does.
- MaxReportElementCount
Gets or sets the maximum number of elements a report definition may contain, or null for unlimited.
On by default (DefaultMaxReportElementCount). An element count is not proportional to input size: a compressed or highly repetitive definition can ask the reader to materialize far more nodes than its bytes suggest.
- MaxReportExpressionAllocation
Gets or sets the maximum number of string characters and array elements one evaluation of a report expression may allocate, or null for unlimited.
On by default (DefaultMaxReportExpressionAllocation). The size of an allocation is an argument the expression chooses —
Space(2000000000)is eighteen characters asking for four gigabytes — so it is charged against the budget before anything is allocated.
- MaxReportExpressionDepth
Gets or sets how deeply a report expression may nest, counted in operators and calls, or null for unlimited.
On by default (DefaultMaxReportExpressionDepth). An expression is attacker-controlled text and its evaluator walks the tree it parses to, so the bound is applied by the parser, at the first layer that walks it: an expression past the limit is refused before any evaluation, and the failure it prevents is a
StackOverflowExceptionno caller can catch.
- MaxReportExpressionSteps
Gets or sets the maximum number of steps one evaluation of a report expression may take, or null for unlimited.
On by default (DefaultMaxReportExpressionSteps). Depth is not work: an aggregate nested inside an aggregate, or a
Lookupin every row, stays shallow and still multiplies the rows it reads.
- MaxReportGroupInstances
Gets or sets the maximum number of group instances one report run may create, counting every instance of every group in every data region, details included, or null for unlimited.
On by default (DefaultMaxReportGroupInstances). A definition chooses how many data regions group the same rows, so the count is not bounded by the data alone.
- MaxReportInstanceNodes
Gets or sets the maximum number of instance nodes one report run may create - group instances, matrix cells and report item occurrences together - or null for unlimited.
On by default (DefaultMaxReportInstanceNodes). Matrix cells are the product of the row and column instances, so the count is quadratic in the data and not bounded by input size.
- MaxReportNestingDepth
Gets or sets the maximum element nesting depth a report definition may reach, or null for unlimited.
On by default (DefaultMaxReportNestingDepth). The failure this prevents is
StackOverflowException, which nocatchcan contain, so the bound is applied by the reader at the first layer that walks the tree.
- MaxReportPages
Gets or sets the maximum number of pages one pagination of a report may produce, or null for unlimited.
On by default (DefaultMaxReportPages). The page size comes from the definition, so a page a fraction of a point tall turns an ordinary report into millions of pages.
- MaxReportSubreportDepth
Gets or sets how deep subreports may nest inside one another, or null for the processor's own ceiling.
On by default (DefaultMaxReportSubreportDepth). A report that names itself as its subreport recurses once per level, each level a whole report run. Null does not remove the bound: report processing never nests deeper than its fixed ceiling of 64, because the failure past it is a stack overflow, which nothing can catch.
- MaxUncompressedPartBytes
Gets or sets the maximum uncompressed size of any single package part in bytes (the zip-bomb guard), or null for unlimited.
On by default (DefaultMaxUncompressedPartBytes). A ZIP entry declares its uncompressed size in metadata the file itself supplies, so the size of the package says nothing about the size of what comes out of it.
- MaxWallClockTime
Gets or sets the maximum allowed wall-clock conversion time, or null for unlimited.
Observed cooperatively, at boundaries between units of work: between conversion stages, and — since resolution and layout learned to call back — at every block, table row, section and rendered page. So the budget is enforced within one unit of work of expiring rather than exactly on it, and a single indivisible operation still runs to completion. It is not a watchdog and does not abort a thread.