patriksimek / vm2

Advanced vm/sandbox for Node.js
MIT License
3.86k stars 293 forks source link

VMError: Operation not allowed on contextified object #491

Closed ppedziwiatr closed 1 year ago

ppedziwiatr commented 1 year ago

Hey,

I'm getting VMError: Operation not allowed on contextified object

image

while trying to run some untrusted code.

The code that fails is:

function createBuffer(that, length) {
    if (kMaxLength() < length) {
      throw new RangeError("Invalid typed array length");
    }
    if (Buffer2.TYPED_ARRAY_SUPPORT) {
      that = new Uint8Array(length);
      that.__proto__ = Buffer2.prototype; // this line fails!
    } else {
      if (that === null) {
        that = new Buffer2(length);
      }
      that.length = length;
    }
    return that;
  }

VM2 configuration:

const vm = new vm2.NodeVM({
          console: 'off',
          sandbox: {
            SmartWeave: swGlobal,
            BigNumber: BigNumber,
            logger: this.logger,
            ContractError: ContractError,
            ContractAssert: function (cond, message) {
              if (!cond) throw new ContractError(message);
            },
            //https://github.com/patriksimek/vm2/issues/484#issuecomment-1327479592
            Uint8Array: Uint8Array,
            atob: atob
          },
          compiler: 'javascript',
          eval: false,
          wasm: false,
          allowAsync: true,
          wrapper: 'commonjs'
        });
XmiliaH commented 1 year ago

This is expected and wanted. Changing the prototype of host objects if forbidden.

ppedziwiatr commented 1 year ago

Oh...right...so - with no other workaround with working with Uint8Array than https://github.com/patriksimek/vm2/issues/484#issuecomment-1327917743 - there's effectively no way to run this code in VM2, right?

XmiliaH commented 1 year ago

What you could try is:

const ProxiedUint8Array = new Proxy(Uint8Array, {
    construct(target, args, newTarget) {
        const original = Reflect.construct(target, args, newTarget);
        Reflect.defineProperty(original, '__proto__', {
            get() {return Reflect.getPrototypeOf(original);},
            set(value) {Reflect.setPrototypeOf(original, value);},
            configurable: true,
            enumerable: false
        });
        return original;
    }
});

const vm = new NodeVM({sandbox: {
    Uint8Array: ProxiedUint8Array
}});
ppedziwiatr commented 1 year ago

Thanks, we will give it a try!

ppedziwiatr commented 1 year ago

This ends with:


'TypeError: Method get TypedArray.prototype.length called on incompatible receiver [object Object]\n' +
    '    at TypedArray.get length [as length] (<anonymous>)\n' +
    '    at get (<anonymous>)\n' +
    '    at VM2 Wrapper.get (/Users/piotrpedziwiatr/projects/redstone-smartcontracts/node_modules/vm2/lib/bridge.js:447:11)\n' +
    '    at get (<anonymous>)\n' +
    '    at VM2 Wrapper.get (/Users/piotrpedziwiatr/projects/redstone-smartcontracts/node_modules/vm2/lib/bridge.js:447:11)\n' +
    '    at get (<anonymous>)\n' +
    '    at VM2 Wrapper.get (/Users/piotrpedziwiatr/projects/redstone-smartcontracts/node_modules/vm2/lib/bridge.js:447:11)\n' +
    '    at Proxy.write (vm.js:1337:25)\n' +
    '    at fromString (vm.js:404:23)\n' +
    '    at from (vm.js:364:14)'
``` ...