Skip to content

Commit 1b4debd

Browse files
committed
Limit decoded result imflation ratio from ABI-encoded data (#4537).
1 parent 6017d3d commit 1b4debd

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

src.ts/abi/abi-coder.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
5353

5454

5555
let defaultCoder: null | AbiCoder = null;
56-
56+
let defaultMaxInflation = 1024;
5757

5858
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
5959
let message = "missing revert data";
@@ -206,7 +206,12 @@ export class AbiCoder {
206206
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
207207
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
208208
const coder = new TupleCoder(coders, "_");
209-
return coder.decode(new Reader(data, loose));
209+
return coder.decode(new Reader(data, loose, defaultMaxInflation));
210+
}
211+
212+
static _setDefaultMaxInflation(value: number): void {
213+
assertArgument(typeof(value) === "number" && Number.isInteger(value), "invalid defaultMaxInflation factor", "value", value);
214+
defaultMaxInflation = value;
210215
}
211216

212217
/**

src.ts/abi/coders/abstract-coder.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,10 +414,17 @@ export class Reader {
414414
readonly #data: Uint8Array;
415415
#offset: number;
416416

417-
constructor(data: BytesLike, allowLoose?: boolean) {
417+
#bytesRead: number;
418+
#parent: null | Reader;
419+
#maxInflation: number;
420+
421+
constructor(data: BytesLike, allowLoose?: boolean, maxInflation?: number) {
418422
defineProperties<Reader>(this, { allowLoose: !!allowLoose });
419423

420424
this.#data = getBytesCopy(data);
425+
this.#bytesRead = 0;
426+
this.#parent = null;
427+
this.#maxInflation = (maxInflation != null) ? maxInflation: 1024;
421428

422429
this.#offset = 0;
423430
}
@@ -427,6 +434,21 @@ export class Reader {
427434
get consumed(): number { return this.#offset; }
428435
get bytes(): Uint8Array { return new Uint8Array(this.#data); }
429436

437+
#incrementBytesRead(count: number): void {
438+
if (this.#parent) { return this.#parent.#incrementBytesRead(count); }
439+
440+
this.#bytesRead += count;
441+
442+
// Check for excessive inflation (see: #4537)
443+
assert(this.#maxInflation < 1 || this.#bytesRead <= this.#maxInflation * this.dataLength, `compressed ABI data exceeds inflation ratio of ${ this.#maxInflation } ( see: https:/\/github.com/ethers-io/ethers.js/issues/4537 )`, "BUFFER_OVERRUN", {
444+
buffer: getBytesCopy(this.#data), offset: this.#offset,
445+
length: count, info: {
446+
bytesRead: this.#bytesRead,
447+
dataLength: this.dataLength
448+
}
449+
});
450+
}
451+
430452
#peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
431453
let alignedLength = Math.ceil(length / WordSize) * WordSize;
432454
if (this.#offset + alignedLength > this.#data.length) {
@@ -445,12 +467,15 @@ export class Reader {
445467

446468
// Create a sub-reader with the same underlying data, but offset
447469
subReader(offset: number): Reader {
448-
return new Reader(this.#data.slice(this.#offset + offset), this.allowLoose);
470+
const reader = new Reader(this.#data.slice(this.#offset + offset), this.allowLoose, this.#maxInflation);
471+
reader.#parent = this;
472+
return reader;
449473
}
450474

451475
// Read bytes
452476
readBytes(length: number, loose?: boolean): Uint8Array {
453477
let bytes = this.#peekBytes(0, length, !!loose);
478+
this.#incrementBytesRead(length);
454479
this.#offset += bytes.length;
455480
// @TODO: Make sure the length..end bytes are all 0?
456481
return bytes.slice(0, length);

0 commit comments

Comments
 (0)