svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.92k stars 514 forks source link

RangeError: cannot push beyond allocated stack #2372

Closed dzonerzy closed 2 years ago

dzonerzy commented 3 years ago

I'm trying to bind a C function to duktape , this function should return an object containing each processes with their loaded modules and each exported function for each module, here's the main C code:

BOOL CALLBACK mtool_enumsymproc(SYMBOL_INFO * pSymInfo, unsigned long SymbolSize, void * UserContext) {
    UNREFERENCED_PARAMETER(UserContext);
    PMODULECTX mctx = (PMODULECTX)UserContext;
    duk_push_number(mctx->Context, (unsigned long long)((pSymInfo->Address - mctx->BaseAddress) + mctx->hModule));
    duk_put_prop_string(mctx->Context, -2, pSymInfo->Name);
    return TRUE;
}

duk_ret_t mtool_do_process_list(duk_context  * ctx) {
    // handle parameters
    duk_require_object(ctx, 0);
    // handle callback for results
    duk_require_function(ctx, 1);
    // handle callback for errors
    duk_require_function(ctx, 2);

    if (!duk_is_object(ctx, 0) ||  !duk_is_function(ctx, 1) || !duk_is_function(ctx, 2)) {
        duk_push_error_object(ctx, -1, "invalid parameters");
        duk_throw_raw(ctx);
        duk_pop(ctx);
        return 0;
    }

    //  save result callback
    void* callback = duk_get_heapptr(ctx, 1);
    // save error callback
    void* error = duk_get_heapptr(ctx, 2);
    // parameters
    duk_bool_t with_modules = FALSE;
    duk_bool_t with_symbols = FALSE;

    duk_enum(ctx, 0, 0);  /* Pushes enumerator object. */
    while (duk_next(ctx, -1, 1)) { 
        char* key = duk_safe_to_string(ctx, -2);
        switch (mtool_hash_string(key)) {
        case HASH_KEY_MODULES:
            with_modules = duk_to_boolean(ctx, -1);
            duk_pop_2(ctx);
            break;
        case HASH_KEY_SYMBOLS:
            with_symbols = duk_to_boolean(ctx, -1);
            duk_pop_2(ctx);
            break;
        default:
            duk_pop_2(ctx);
        }

    }
    duk_pop(ctx);

    // save call result
    duk_int_t rc;

    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;

    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        duk_push_heapptr(ctx, error);
        duk_push_error_object(ctx, INVALID_HANDLE_VALUE, "unable to create snapshot");
        duk_call(ctx, 1);
        duk_pop_n(ctx, 2);
        return 0;
    }

    // Set the size of the structure before using it.
    pe32.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hProcessSnap, &pe32))
    {
        duk_push_heapptr(ctx, error);
        duk_push_error_object(ctx, -1, "unable to get first process");
        duk_call(ctx, 1);
        duk_pop_n(ctx, 2);
        CloseHandle(hProcessSnap);          // clean the snapshot object
        return 0;
    }

    do
    {
        // craft the process obj
        duk_push_heapptr(ctx, callback);
        duk_push_object(ctx);
        // set the process name
        duk_push_string(ctx, pe32.szExeFile);
        duk_put_prop_string(ctx, -2, "name");
        // set the process id
        duk_push_int(ctx, pe32.th32ProcessID);
        duk_put_prop_string(ctx, -2, "pid");

        if (with_modules) {
            HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID);
            HMODULE modules[128] = { 0 };
            DWORD modulesCount;
            CHAR buffer[MAX_PATH];

            if (hProc != NULL) {
                duk_push_object(ctx);
                if (EnumProcessModules(hProc, (HMODULE*)memset(modules, 0, 128), 128, &modulesCount)) {
                    modulesCount = modulesCount <= 128 ? modulesCount : 128;
                    for (int i = 0; i < modulesCount; ++i) {
                        if (GetModuleFileNameExA(hProc, modules[i], (LPSTR)memset(buffer, 0, MAX_PATH), MAX_PATH)) {
                            if (modules[i] != NULL) {
                                duk_push_object(ctx);
                                duk_push_number(ctx, (duk_double_t)((unsigned long long)modules[i]));
                                duk_put_prop_string(ctx, -2, "baseaddr");
                                if (with_symbols && SymInitialize(hProc, NULL, FALSE)) {

                                    DWORD64 BaseOfDll = SymLoadModuleEx(hProc, NULL, buffer, NULL, 0, 0, NULL, 0);
                                    if (BaseOfDll != NULL)
                                    {
                                        MODULECTX mctx;
                                        mctx.Context = ctx;
                                        mctx.BaseAddress = BaseOfDll;
                                        mctx.hModule = modules[i];

                                        duk_push_object(ctx);
                                        SymEnumSymbols(hProc, BaseOfDll, "*", mtool_enumsymproc, &mctx);
                                        duk_put_prop_string(ctx, -2, "symbols");
                                    }
                                    SymCleanup(hProc);
                                }
                                mtool_basename(buffer, buffer);
                                mtool_tolowercase(buffer);
                                duk_put_prop_string(ctx, -2, buffer);
                            }
                        }
                    }
                }
                duk_put_prop_string(ctx, -2, "modules");
                CloseHandle(hProc);
            }
        }

        // call the callback routine
        rc = duk_pcall(ctx, 1);
        //check for errors
        if (rc != 0) {
            // craft the exception
            duk_push_heapptr(ctx, error);
            duk_push_error_object(ctx, rc, "invalid result callback");
            // call the error callback
            duk_call(ctx, 1);
            // clean stack
            duk_pop_n(ctx, 2);
            return 0;
        }
        else {
            // clean
            //duk_pop(ctx);
            duk_bool_t val = duk_get_boolean(ctx, -1);
            if (val) {
                CloseHandle(hProcessSnap);
                return 0;
            }
        }
    } while (Process32Next(hProcessSnap, &pe32));
    // close handle
    CloseHandle(hProcessSnap);
    return 0;
}

and this is the JS code i'm trying to run:

try {
    Process.ListProcesses({"modules": true, "symbols": true}, function(element){
        if(element.name == "Calculator.exe") {
            print("Process: " + element.name + " PID: " + element.pid)
            Object.keys(element.modules).forEach(function(mod) {
                print(" - " + mod + " => " + hex(element.modules[mod].baseaddr))
                Object.keys(element.modules[mod].symbols).forEach(function(sym){
                    print("     - " +  sym + " => " + hex(element.modules[mod].symbols[sym]))
                })
            })
            return true;
        }
        return false;
    },
    function(err) {
        print(err)
    });
}catch(exc) {
    print(exc)
}
dzonerzy commented 3 years ago

The error happen at:

duk_bool_t val = duk_get_boolean(ctx, -1);

basically here i'm checking the result of the callback (the one which receive each process object) it it's true i want to exit without looping all the other processes , that's works fine until the right process is matched, so i think I've messed up something inside the symbols loop

svaarala commented 3 years ago

Is the value stack intended to grow significantly in the C code dealing with the arguments? If so, you may need to extend the value stack manually, see https://duktape.org/guide.html#programming.5.

If the value stack is intended to remain small, then typically some loop is not popping all temporaries and accidentally allows the value stack to grow. One can detect this situation usually fairly easily by debug printing duk_get_top() at the top of each loop. The fix is either to find the missing pop(s), or the rework the loop so that one explicitly resets the top to a known value e.g. at the top of the loop to automatically pop any temporaries from the previous round.