Limit decoded result imflation ratio from ABI-encoded data (#4537).
This commit is contained in:
parent
6017d3d39a
commit
1b4debd4a9
@ -53,7 +53,7 @@ const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
|
|||||||
|
|
||||||
|
|
||||||
let defaultCoder: null | AbiCoder = null;
|
let defaultCoder: null | AbiCoder = null;
|
||||||
|
let defaultMaxInflation = 1024;
|
||||||
|
|
||||||
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
|
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
|
||||||
let message = "missing revert data";
|
let message = "missing revert data";
|
||||||
@ -206,7 +206,12 @@ export class AbiCoder {
|
|||||||
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
|
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
|
||||||
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
|
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
|
||||||
const coder = new TupleCoder(coders, "_");
|
const coder = new TupleCoder(coders, "_");
|
||||||
return coder.decode(new Reader(data, loose));
|
return coder.decode(new Reader(data, loose, defaultMaxInflation));
|
||||||
|
}
|
||||||
|
|
||||||
|
static _setDefaultMaxInflation(value: number): void {
|
||||||
|
assertArgument(typeof(value) === "number" && Number.isInteger(value), "invalid defaultMaxInflation factor", "value", value);
|
||||||
|
defaultMaxInflation = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -414,10 +414,17 @@ export class Reader {
|
|||||||
readonly #data: Uint8Array;
|
readonly #data: Uint8Array;
|
||||||
#offset: number;
|
#offset: number;
|
||||||
|
|
||||||
constructor(data: BytesLike, allowLoose?: boolean) {
|
#bytesRead: number;
|
||||||
|
#parent: null | Reader;
|
||||||
|
#maxInflation: number;
|
||||||
|
|
||||||
|
constructor(data: BytesLike, allowLoose?: boolean, maxInflation?: number) {
|
||||||
defineProperties<Reader>(this, { allowLoose: !!allowLoose });
|
defineProperties<Reader>(this, { allowLoose: !!allowLoose });
|
||||||
|
|
||||||
this.#data = getBytesCopy(data);
|
this.#data = getBytesCopy(data);
|
||||||
|
this.#bytesRead = 0;
|
||||||
|
this.#parent = null;
|
||||||
|
this.#maxInflation = (maxInflation != null) ? maxInflation: 1024;
|
||||||
|
|
||||||
this.#offset = 0;
|
this.#offset = 0;
|
||||||
}
|
}
|
||||||
@ -427,6 +434,21 @@ export class Reader {
|
|||||||
get consumed(): number { return this.#offset; }
|
get consumed(): number { return this.#offset; }
|
||||||
get bytes(): Uint8Array { return new Uint8Array(this.#data); }
|
get bytes(): Uint8Array { return new Uint8Array(this.#data); }
|
||||||
|
|
||||||
|
#incrementBytesRead(count: number): void {
|
||||||
|
if (this.#parent) { return this.#parent.#incrementBytesRead(count); }
|
||||||
|
|
||||||
|
this.#bytesRead += count;
|
||||||
|
|
||||||
|
// Check for excessive inflation (see: #4537)
|
||||||
|
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", {
|
||||||
|
buffer: getBytesCopy(this.#data), offset: this.#offset,
|
||||||
|
length: count, info: {
|
||||||
|
bytesRead: this.#bytesRead,
|
||||||
|
dataLength: this.dataLength
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
|
#peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
|
||||||
let alignedLength = Math.ceil(length / WordSize) * WordSize;
|
let alignedLength = Math.ceil(length / WordSize) * WordSize;
|
||||||
if (this.#offset + alignedLength > this.#data.length) {
|
if (this.#offset + alignedLength > this.#data.length) {
|
||||||
@ -445,12 +467,15 @@ export class Reader {
|
|||||||
|
|
||||||
// Create a sub-reader with the same underlying data, but offset
|
// Create a sub-reader with the same underlying data, but offset
|
||||||
subReader(offset: number): Reader {
|
subReader(offset: number): Reader {
|
||||||
return new Reader(this.#data.slice(this.#offset + offset), this.allowLoose);
|
const reader = new Reader(this.#data.slice(this.#offset + offset), this.allowLoose, this.#maxInflation);
|
||||||
|
reader.#parent = this;
|
||||||
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read bytes
|
// Read bytes
|
||||||
readBytes(length: number, loose?: boolean): Uint8Array {
|
readBytes(length: number, loose?: boolean): Uint8Array {
|
||||||
let bytes = this.#peekBytes(0, length, !!loose);
|
let bytes = this.#peekBytes(0, length, !!loose);
|
||||||
|
this.#incrementBytesRead(length);
|
||||||
this.#offset += bytes.length;
|
this.#offset += bytes.length;
|
||||||
// @TODO: Make sure the length..end bytes are all 0?
|
// @TODO: Make sure the length..end bytes are all 0?
|
||||||
return bytes.slice(0, length);
|
return bytes.slice(0, length);
|
||||||
|
Loading…
Reference in New Issue
Block a user