Closed Bug 1013960 Opened 11 years ago Closed 11 years ago

FF24esr - Exception catched when I use formelement.idorname syntax in a JS-code called by JS_CallFunctionValue()

Categories

(Core :: XPConnect, defect)

x86_64
Windows 7
defect
Not set
normal

Tracking

()

RESOLVED INVALID

People

(Reporter: sandor.banki-horvath, Unassigned)

Details

User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0 (Beta/Release) Build ID: 20140421162246 Steps to reproduce: <HTML> <HEAD> <SCRIPT> function Test() { try { alert(window.MainForm.helpID.value); } catch (xe) { alert(xe.message); } } function Test2() { try { alert(document.getElementById('helpID').value); } catch (xe) { alert(xe.message); } } </SCRIPT> </HEAD> <BODY> <FORM name="MainForm" > <INPUT value="1" id="helpID"> </FORM> <A href="JavaScript:Test();">click here</A> </BODY> </HTML> - Clicking "click here" works correct. - I have injected a cpp-dll into this HTML page: typedef __int64 JSVAL; void* pJSObj = GetJSObjectFrom_nsIDOMWindow(pWindow); if(pJSObj) { JSVAL result; JSVAL jsval = 0; JS_GetProperty(pJSContext, pJSObj, "Test", &jsval); JS_CallFunctionValue(pJSContext, pJSObj, jsval, 0, NULL, &result); } - Running this code causes "Permission denied" exception. Running this code with "Test2" works correct. Actual results: "Permission denied" exception is catched Expected results: alert "1"
(In reply to Sandor from comment #0) > - I have injected a cpp-dll into this HTML page: Could you elaborate on this part a bit? But the real important question here I think is what is pJSContext in your code and where do you get it from? I have the feeling that the pJSContext pointing to some random compartment, which is equivalent with calling JS_GetProperty from a different scope (which is likely cross origin hence the permission denied). You should first enter the compartment of the window with that JSContext.
Flags: needinfo?(sandor.banki-horvath)
Dll is injected with SetWindowsHookEx(). Tha main document interface comes from xul.dll by NS_GetServiceManager exported function, across "@mozilla.org/fuel/application;1" service. pJSContext comes from nsIDocument (there is only 1 document in this page): nsIScriptGlobalObject* pSGO = pIDoc->GetScriptGlobalObject(); if(pSGO) { nsIScriptContext* pSC = pSGO->GetContext(); if(pSC) { void* pJSContext = pSC->GetNativeContext(); } } I have used m_pCompartment = JS_EnterCompartment(pJSContext, pJSGlobalObject); before calling any function with pJSContext and I have called JS_LeaveCompartment(pJSContext, m_pCompartment); after that.
Flags: needinfo?(sandor.banki-horvath)
> m_pCompartment = JS_EnterCompartment(pJSContext, pJSGlobalObject); How is pJSGlobalObject related to any of the rest of this stuff? Are you running this in a debug build? If not, you should: that will immediately tell you about compartment mismatches.
The open source code contains this declaration(jsapi.h): extern JS_PUBLIC_API(JSCompartment *) JS_EnterCompartment(JSContext *cx, JSObject *target); I thought that I can use pJSGlobalObject as 'target' parameter. pJSGlobalObject comes also from nsIDocument: nsIScriptGlobalObject* pSGO = pIDoc->GetScriptGlobalObject(); if(pSGO) { nsIScriptContext* pSC = pSGO->GetContext(); if(pSC) { void* pJSGlobalObject = pSC->GetNativeGlobal(); } } If it is not correct, what I should use instead? I use release version of Firefox 24.5esr.
> void* pJSGlobalObject = pSC->GetNativeGlobal(); Is that done right before the code in comment 2? Seriously, if you want help, either run a debug build or post the entirety of the relevant code. Mind-reading is time-consuming and pretty annoying.
I'll try to explain the things that should be taken care of before/after calling any JS API from C++. - The JSContext has to be pushed to the JS stack. - The JSContext has to enter the compartment of the global it operates on. (JS_EnterCompartment) - JSContext should enter request, I'm not sure what API is for ff24.5esr the current API is JSAutoRequest which is done automatically during the first step. So hopefully you can ignore this step... - doing all the JS_GetProperty(pJSContext, pJSObj, "Test", &jsval); JS_CallFunctionValue(pJSContext, pJSObj, jsval, 0, NULL, &result); part - JSContext should enter the previous compartment - JSContext should be popped from the stack So... was the context pushed to the stack? And yeah, running it in a debug build would help a lot probably.
"was the context pushed to the stack?" - No. Witch JS_function and/or interface should I use for push and pop? "SContext should enter the previous compartment" -"enter"? not "leave"? -does "previous" mean the JSContext we use in all step? What I got by the code in Comment2.
(In reply to Sandor from comment #7) > "was the context pushed to the stack?" > - No. Witch JS_function and/or interface should I use for push and pop? AutoPushJSContext, but I'm not sure it's available... > "SContext should enter the previous compartment" > -"enter"? not "leave"? "Leave" does that, it's fine. Have you tried running it with a debug build?
Where can I download debug build from?
You can just download one from the continuous integration boxes, no? For Windows, for example, http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/mozilla-esr24-win32-debug/1400617938/ is a recent ESR24 debug build. Note that you need to make sure to compile your code with DEBUG defined to run against that build.
I have installed this debug version, it shows debug informations in another window. I would like to debug the code but I have found only a zip that contains .sym files. The Visual Studio can use only .pdb files. I there any way to get .pdb files for this FF-version?
(In reply to Sandor from comment #12) > I have installed this debug version, it shows debug informations in another > window. > I would like to debug the code but I have found only a zip that contains > .sym files. The Visual Studio can use only .pdb files. I there any way to > get .pdb files for this FF-version? Have you tried running your example in it? Even if you don't run it from a debugger it should hit some assertions (that do not exist in the release builds) but would be helpful here... For debugging, I don't know... we have a symbol server: https://developer.mozilla.org/en-US/docs/Using_the_Mozilla_symbol_server but I have no idea how to use it or if it will help you or not. I just work with my own builds usually...
This symbol server is also unusable for this debug version :( I have tried running my example but it make a crash at JS_EnterCompartment - probably because of the version-dependent offset of some interface method or data member. Theese offsets we usually map for a new FF version with the debugger (it can get the proper pdb files from the mozilla symbol server). My question: If I build the Firefox for myself, will I get also pdf files for debugging?
will I get also pdB files for debugging?
(In reply to Sandor from comment #14) \> I have tried running my example but it make a crash at JS_EnterCompartment - File/line number? Is there a stack on the console? > probably because of the version-dependent offset of some interface method or > data member. Theese offsets we usually map for a new FF version with the > debugger (it can get the proper pdb files from the mozilla symbol server). I don't understand. It's the same version as the release one, just a debug version. How could the offset be different of any interface method? > > My question: If I build the Firefox for myself, will I get also pdf files > for debugging? Yes, just make sure that in the mozconfig file you turn off optimisation and turn on debugging.
There is one console message while crashing: "Assertion failure: cx->runtime()->requestDepth || isHeapBusy(), at c:/builds/moz2_slave/m-esr24-w32-d-0000000000000000/build/js/src/jscntxt.cpp:1529" Visual Studio cannot show any stack info (without pdb), only "mozjs.dll" is written. "How could the offset be different" - e.g. #ifdef DEBUG virtual char* GetDebugInfo() = 0; #endif
If you're getting that assertion, that means you didn't enter a request before using JSAPI. You should probably start by fixing that. Past that, it's _possible_ that the symbol server has symbols for the builds in http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2014-05-25-mozilla-esr24-debug/ (which are nightly builds, not just per-checkin builds). Though I looking at the build logs, I'm not too hopeful.
Thanks Boris, debugging works fine! :) Crash is caused by the first line of the constructor in this block: " #if defined JS_THREADSAFE && defined DEBUG JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx) : cx(cx) { JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy()); cx->runtime()->assertValidThread(); cx->runtime()->checkRequestDepth++; } JS::AutoCheckRequestDepth::~AutoCheckRequestDepth() { JS_ASSERT(cx->runtime()->checkRequestDepth != 0); cx->runtime()->checkRequestDepth--; } #endif " - "defined DEBUG" shows why can my code run without crash with the release version. - if "cx->runtime()->requestDepth || cx->runtime()->isHeapBusy()" is problematic in my code, then may it cause my "permission denied" exception? And you have metioned "didn't enter a request before using JSAPI". So, what should I do before JS_EnterCompartment? Exactly witch JS_exportedfunction or interface-method should I use first?
(In reply to Boris Zbarsky [:bz] from comment #18) > If you're getting that assertion, that means you didn't enter a request > before using JSAPI. You should probably start by fixing that. Which should happen automatically when pushing a cx to the stack... so I'm still afraid that we need to do a cx push some time, I just don't know how is it possible with a public API. Anyway, JSAutoRequest should do the request start/stop for you, let's see if it fixes it.
Or JS_BeginRequest / JS_EndRequest if the JSAutoRequest RAII helper is not exposed...
"JS_BeginRequest / JS_EndRequest" has resolved the crash at EnterCompartment. So my code runs with Test2() but crashes with Test(). I have debugged this crash and I've seen that the debug version make this crash purposely at "Compartment mismatch": jscntxtinlines.h Ln 144. static void fail(JSCompartment *c1, JSCompartment *c2) { printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2); MOZ_CRASH(); } - I've got JSContext /GetNativeContext()/ and JSGlobalObject /GetNativeGlobal()/ from the ActiveTab's document's nsIScriptGlobalObject's nsIScriptContext - object parameter of JS_CallFunctionValue() is from pXPConnect->WrapNative(pJSContext, pJSGlobalObject, pUnk, __uuidof(nsIDOMWindow), &m_pJSObjectHolder); where pUnk is IUnknown* of the nsIDOMWindow of the ActiveTab's document
I see, WrapNative() also calls entercompartment(). Should I use other way to get JSObject from nsIDOMWindow? Or other way to get JSGlobalObject. Or JSContext?
> at "Compartment mismatch": Yes, see comment 1! > Should I use other way to get JSObject from nsIDOMWindow? See comment 5. It's not worth my time trying to guess what your code looks like. Just post the actual code, and then it will probably take about 30 seconds to tell you what the problem is... But in short, all arguments to a JSAPI call (the JSContext, all objects, etc) must be in the same compartment. If you're getting the window via WrapNative() like that, then you should enter the compartment of the resulting object on the context before doing anything else.
typedef __int64 JSVAL; HMODULE hModule=::GetModuleHandle(_T("xul.dll")); void* pProcAddr = ::GetProcAddress(hModule, _T("NS_GetServiceManager")); CnsIServiceManagerPtr pServMan; ((NS_GetServiceManager_Func)pProcAddr)(&pServMan); CnsIXPConnectPtr pXPConnect; pServMan->GetServiceByContractID("@mozilla.org/js/xpc/XPConnect;1", __uuidof(nsIXPConnect), (void**)&pXPConnect); CnsIWindowWatcherPtr pWindowWatcher; pServMan->GetServiceByContractID("@mozilla.org/embedcomp/window-watcher;1", __uuidof(nsIWindowWatcher), (void**)&pWindowWatcher); CnsIDOMWindowPtr pAW; pWindowWatcher->GetActiveWindow(&pAW); CnsIDOMWindowPtr pWind = GetSubFrameByIndex(pAW, 2); CnsIDOMDocumentPtr pDoc; pWind->GetDocument(&pDoc); CnsIDocumentPtr pIDoc(pDoc); nsIScriptGlobalObject* pSGO = GetScriptGlobalObject(pIDoc); nsIScriptContext* pSC = pSGO->GetContext(); void* pJSContext = pSC->GetNativeContext(); void* pJSGlobalObject = pSC->GetNativeGlobal(); JS_BeginRequest(pJSContext); void* pCompartment = JS_EnterCompartment(pJSContext, pJSGlobalObject); CUnknownPtr pUnk(pWind); CnsIXPConnectJSObjectHolderPtr pJSObjectHolder; pXPConnect->WrapNative(pJSContext, pJSGlobalObject, pUnk, __uuidof(nsIDOMWindow), &pJSObjectHolder); void* pJSObj; GetJSObjectFromHolder(pJSObjectHolder, &pJSObj); JSVAL jsval; JS_GetProperty(pJSContext, pJSObj, "Test", &jsval); JSVAL result; JS_CallFunctionValue(pJSContext, pJSObj, jsval, 0, NULL, &result); JS_LeaveCompartment(pJSContext, pCompartment); JS_EndRequest(pJSContext);
Yes, you need to enter the compartment of pJSObj, not of pJSGlobalObject. There's no particular reason the latter should be anything useful, afaik.
If I call JS_EnterCompartment, I have no pJSObj yet. How should I modify this code exactly?
After you get pJSObj, you enter its compartment. As in, you need two JS_EnterCompartment calls: one before the WrapNative, one after. And two JS_LeaveCompartment calls.
This formula I have tried after your Comment 25 :) Did not help.
OK, did you still get a compartment assertion? If so, with what stack?
Yes, I get the same assertion and crash as I've written in my Comment 23. Stack shows that this is called by assertSameCompartmentDebugOnly(JSContext *cx, const T1 &t1) function.
Yes, but what's further up the stack?
JS_CallFunctionValue(JSContext *cx, JSObject *objArg, jsval fval, unsigned argc, jsval *argv, jsval *rval) js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv, Value *rval) js::RunScript(JSContext *cx, RunState &state) assertSameCompartmentDebugOnly(JSContext *cx, const T1 &t1) - If I use Test2() JS function with this code then there is no compartment-assertion or crash, only JavaSript-error 0x80040111 is written on the consolet but the JS-assert works correct. - Tip: Is there any other way to get JSObject from nsIDOMWindow instead of WrapNative()?
JS-alert works correct :)
"JS-alert works correct :)" is only a corrction of mistaken "JS-assert" in Comment 34.
Corrected stack: JS_CallFunctionValue(JSContext *cx, JSObject *objArg, jsval fval, unsigned argc, jsval *argv, jsval *rval) js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv, Value *rval) js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct) js::RunScript(JSContext *cx, RunState &state) Interpret(JSContext * cx, js::RunState & state) assertSameCompartmentDebugOnly(JSContext *cx, const T1 &t1) - 'Interpret' maybe the reason of the different behavior if trying different JS-codes.
> - Tip: Is there any other way to get JSObject from nsIDOMWindow instead of WrapNative()? For your purposes, no. That assert stack shows that the assert is not in your JSAPI code per se but in the actual script. Can you narrow down which part is causing the assertion? Is it the "window.MainForm" bit? Is it the .helpID bit after that?
1. alert(window.MainForm); - works correct (but the console shows error 0x80040111 and a line-number in the js-file that is the alert() in the catch-branch, that has not run at all) 2. alert(window.MainForm.helpID); - same error: fail(JSCompartment *c1, JSCompartment *c2) -'global_' member of c1 and c2 and my pJSGlobalObject are 3 different value. -CompartmentDepth of c1 is as many as EnterCompartment I've called. CompartmentDepth of c2 is 0.
Alright. I guess you get to look at the globals of c1 and c2 and try to figure out what those are, exactly...
Should I see anything or should I waiting for you while you are trying to figure out something? :)
You should be debugging what the globals involved are and why they're different. I'm not the one that has this in a debugger; you are.
I don't know if the 3 different value is correct or not. That know the developers of the mozilla-code. You see my code. If this sample is not a mozilla bug then tell me, what is the mistake in my code. You have promissed for me 30 seconds to explore my mistake :)
> I don't know if the 3 different value is correct or not. The point is, the globals of c1 and c2 are some objects. What are they? What is the getClass()->name on them, for a start? > You have promissed for me 30 seconds to explore my mistake :) "Trying to use JSAPI". ;)
Actually, one other thing you're definitely not doing that you should: you're not pushing your JSContext on the XPConnect JSContext stack. You need to do that. Bobby, what's a sane way to do that from outside libxul?
Flags: needinfo?(bobbyholley)
But note that I don't see anything in the named getter we shipped in 24 (which had webidl form) that would involve examining the JSContext stack. So that might not help either; you still want to examine the globals for those mismatched compartments.
"Class()->name": I don't see any 'class' or 'name' member in c1 and c2 object when mozilla-code breaks. "Pushing JSContext": Does Bobby read this forum? :)
(In reply to Boris Zbarsky [:bz] from comment #45) > Actually, one other thing you're definitely not doing that you should: > you're not pushing your JSContext on the XPConnect JSContext stack. You > need to do that. > > Bobby, what's a sane way to do that from outside libxul? There isn't one. This is precisely why we've stopped exporting JS symbols outside of libxul (see bug 920731). It is now forbidden to interact with JSAPI from external binary components.
Flags: needinfo?(bobbyholley)
> I don't see any 'class' or 'name' member in c1 and c2 object when mozilla-code breaks. You want something like c1->global_.value->getClass()->name. Did you miss the part about "the globals of c1 and c2"? And if it's "Window" then you want: ((nsGlobalWindow*)((XPCWrappedNative*)c1->global_.value->getPrivate())->mIdentity)->mDoc.mRawPtr->mDocumentURI.mRawPtr->mSpec Or whatever the equivalent is in your debugger. > Does Bobby read this forum? :) This isn't a forum, it's a bug database. And Bobby responds to needinfo requests, yes.
c1->global_.value->getClass()->name : "Window" c2->global_.value->getClass()->name : "global_for_XPCJSContextStack_SafeJSContext" ' And if it's "Window" ' part I cannot see in the debugger. But it seems to me that the stack pushing will be the solution really. I am trying to call XPCJSContextStack::Push() by the memory info...
Heureka! :) XPCJSContextStack::Push() has resolved the problem, both of Test() and Test2() works. But very big problem yet that there is no interface for us to support Firefox by Oracle-UPK product. My current operable code contains relative function-addresses that will be changed in every subversion. Please tell me, witch is the correct order: XPCJSContextStack::Push() , JS_BeginRequest, JS_EnterCompartment ?
JS_BeginRequest first, then XPCJSContextStack::Push, then JS_EnterCompartment.
Status: UNCONFIRMED → RESOLVED
Closed: 11 years ago
Resolution: --- → INVALID
Thanks a lot for your help!
You're welcome!
(In reply to Sandor from comment #53) > Thanks a lot for your help! Hi Sandor, Are bale to use XPCJSContextStack::Push() in FF31?
(In reply to Mohamed Jaffar from comment #55) > Are bale to use XPCJSContextStack::Push() in FF31? No. Sandor is working on esr24. The ability to push from outside of libxul was removed in bug 978995.
You need to log in before you can comment on or make changes to this bug.