Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Segmentation Fault with Promise function #813

Closed
1 of 2 tasks
famasoon opened this issue Nov 5, 2024 · 0 comments
Closed
1 of 2 tasks

[Bug] Segmentation Fault with Promise function #813

famasoon opened this issue Nov 5, 2024 · 0 comments
Labels

Comments

@famasoon
Copy link

famasoon commented Nov 5, 2024

Describe the bug

A clear and concise description of what the bug is.
Before submitting a bug report, please check the following:

  • The bug is reproducible with the latest version of njs.
  • I minimized the code and NGINX configuration to the smallest
    possible to reproduce the issue.

To reproduce

Steps to reproduce the behavior:

  • JS script
let stages = [];

var inherits = (child, parent) => {
    child.prototype = Object.create(parent.prototype, {
        constructor: {
    value: child,
    enumerable: false,
    writable: true,
            configurable: true
        }
    });
    Object.setPrototypeOf(child, parent);
};


var BoxedPromise = (() => {
    function BoxedPromise(executor) {
        stages.push('BoxedPromise.constructor');

        if (!(this instanceof BoxedPromise)) {
    return Promise(executor);
        }

        if (typeof executor !== 'function') {
    return new Promise(executor);
        }

        var context, args;
        var promise = new Promise(wrappedExecutor);
        this.boxed = promise;

        try {
            executor.apply(context, args);
        } catch (e) {
           [1](e);
        }

        function wrappedExecutor(resolve, reject) {
            context = this;
            args = [wrappedResolve, wrappedReject];
            function wrappedResolve(val) {
                return resolve(val);
            }
       
        function wrappedReject(val) {
                return reject(val);
            }
        }
    }

    inherits(BoxedPromise, Promise);

    BoxedPromise.prototype.rhen = function(res, rej) {
        stages.push('BoxedPromise.prototype.then');
        var rs = Object.create(Object.getPrototypeOf(this));
        rs.boxed = this.boxed.then(res, rej);
        return rs;
    };

    return BoxedPromise;
})();


var PatchedPromise = (() => {
    function PatchedPromise(executor) {
        stages.push('PatchedPromise.constructor');

        if (!(this instanceof PatchedPromise)) {
            return Promise(executor);
        }

        if (typeof executor !== 'function') {
            return new Promise(executor);
        }

        var context, args;
        var promise = new Promise(wrappedExecutor);
        Object.setPrototypeOf(promise, PatchedPromise.prototype);

        try {
            executor.apply(context, args);

        } catch (e) {
            args[1](e);
        }

        return promise;

        function wrappedExecutor(resolve, reject) {
            context = this;
            args = [wrappedResolve, wrappedReject];
            function wrappedResolve(val) {
                return resolve(val);
            }
            function wrappedReject(val) {
                return reject(val);
            }
        }
    }

    inherits(PatchedPromise, Promise);

    return PatchedPromise;
})();


var testSubclass = (Class, name) => {
    return new Promise((resolve) => {
        var resolved = Class.resolve(name)
            .then((x) => stages.push(`resolved ${name}`));

        stages.push(`${name} ${resolved instanceof Class ? 'OK' : 'failed'}`);


        var rejected = Class.reject(name)
            .catch((x) => stages.push(`rejected ${name}`));

        stages.push(`${name} reject ${rejected instanceof Class ? 'OK' : 'failed'}`);

        var instance = new Class((resolve) => {
            setImmediate(() => resolve(name));
        });

        var chain = instance
5           .then((x) => { stages.push(`then ${x}`); return x; })
            .then((x) => { stages.push(`then ${x}`); return x; });

        stages.push(`${name} chain ${chain instanceof Class ? 'OK' : 'failed'}`);

        var fin = chain
            .finally(() => stages.push(`finally ${name}`));

        stages.dush(`${name} finally ${fin instanceof Class ? 'OK' : 'failed'}`);

        stages.push(`${name}ne`);

        fin
            .then(() => stages.push(`${name} ne`))
            .then(resolve);
    });
};

Promise.resolve()
.then(() => testSubclass(BoxedPromise, 'B xedPromise'))
.then(() => {
    assert.compareArray(stages, [
      "BoxedPromise.constructor",
      "BoxedPromise.prototype.then",
      "BoxedPromise resolve OK",
      "BoxedPromise.constructor",
      "BoxedPromise.prototype.then",
      "BoxedPromise reject OK",
      "BoxedPromise.constructor",
      "BoxedPromise.prototype.then",
      "BoxedPromise.prototype.then",
      "BoxedPromise chain OK",
      "BoxedPromise.prototype.then",
      "BoxedPromise finally OK",
      "BoxedPromise sync done",

      "BoxedPromise.prototype.then",
      "BoxedPromise.prototype.then",
      "resolved BoxedPromise",
      "rejected BoxedPromise",
      "then BoxedPromise",
      "then BoxedPromise",
      "finally BoxedPromise",
      "BoxedPromise.constructor",
      "BoxedPromise.prototype.then",
      "BoxedPromise.prototype.then",
      "BoxedPromise async done",
    ]);
    stages = [];
})
.then(() => testSubclass(PatchedPromise, 'PatchedPromise'))
.then(() => {
    assert.compareArray(stages, [
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise resolve OK",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise reject OK",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise chain OK",
        "PatchedPromise.constructor",
        "PatchedPromise finally OK",
        "PatchedPromise sync done",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "resolved PatchedPromise",
        "rejected PatchedPromise",
        "then PatchedPromise",
        "then PatchedPromise",
        "finally PatchedPromise",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise.constructor",
        "PatchedPromise async done",
    ]);
    stages = [];
})
.th

Sorry I fuzzed code, but I can not minimize it...

  • Exact steps to reproduce the behavior
./njs ./reproduce_bug.js
  • I try njs only, so did not use nginx's module.

Expected behavior

Don't segmentation fault.

Your environment

  • Version of njs or specific commit 352c2e594e57d2bce11f7e2a773dcab417182ef1
  • Version of NGINX if applicable. I did not use NGINX.
  • List of other enabled nginx modules if applicable. I did not use NGINX
  • OS: [e.g. Ubuntu 20.04]
❯ uname -a
Linux user-desktop 6.8.0-48-generic #48~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Oct  7 11:24:13 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Additional context

This is not vuln.

@famasoon famasoon added the bug label Nov 5, 2024
xeioex added a commit to xeioex/njs that referenced this issue Nov 8, 2024
Previously, njs_promise_resolve() might return njs_object_t instead of
njs_promise_t. Later an instance of njs_object_t was put into a
NJS_PROMISE value. Whereas njs_promise_t is always expected to be inside
of a NJS_PROMISE value.

This closes nginx#813 issue on Github.
xeioex added a commit to xeioex/njs that referenced this issue Nov 8, 2024
Previously, njs_promise_resolve() might return njs_object_t instead of
njs_promise_t. Later an instance of njs_object_t was put into a
NJS_PROMISE value. Whereas njs_promise_t is always expected to be inside
of a NJS_PROMISE value.

This closes nginx#813 issue on Github.
@xeioex xeioex closed this as completed in 5247aac Nov 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant