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 defaultMaxInflation = 1024;
|
||||
|
||||
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
|
||||
let message = "missing revert data";
|
||||
@ -206,7 +206,12 @@ export class AbiCoder {
|
||||
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
|
||||
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
|
||||
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;
|
||||
#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 });
|
||||
|
||||
this.#data = getBytesCopy(data);
|
||||
this.#bytesRead = 0;
|
||||
this.#parent = null;
|
||||
this.#maxInflation = (maxInflation != null) ? maxInflation: 1024;
|
||||
|
||||
this.#offset = 0;
|
||||
}
|
||||
@ -427,6 +434,21 @@ export class Reader {
|
||||
get consumed(): number { return this.#offset; }
|
||||
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 {
|
||||
let alignedLength = Math.ceil(length / WordSize) * WordSize;
|
||||
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
|
||||
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
|
||||
readBytes(length: number, loose?: boolean): Uint8Array {
|
||||
let bytes = this.#peekBytes(0, length, !!loose);
|
||||
this.#incrementBytesRead(length);
|
||||
this.#offset += bytes.length;
|
||||
// @TODO: Make sure the length..end bytes are all 0?
|
||||
return bytes.slice(0, length);
|
||||
|
Loading…
Reference in New Issue
Block a user