The crash occurs because js::ScopeCoordinateName returns nullptr. The only way it can do that is if |id| is zero at http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/vm/ScopeObject.cpp#l96.
Unfortunately, it works fine in a debug build. I added a "MOZ_RELEASE_ASSERT(!JSID_IS_ZERO(id))" after http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/vm/ScopeObject.cpp#l89 so I could crash Firefox when this happens and catch it in gdb. At the point we hit this assert in gdb, |sc| has already been optimised out. However, by examining the incoming bytecode: (gdb) p *pc $2 = 136 '\210' (gdb) x pc+1 0xb68b2e2: 0x00000000 ... we see that this is a JSOP_GETALIASEDVAR instruction and the |slot| argument is 0x000000. After adding some printf's to http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/vm/ScopeObject.cpp#l72, I verified that no properties with slot=0 ever exist. I added further asserts in js::frontend::BytecodeEmitter::emitScopeCoordOp() and verified that this instruction sequence was not being emitted here. After creating a small helper function to scan the bytecode at various stages in BytecodeEmitter, I managed to narrow the point at which this instruction sequence is emitted to js::frontend::BytecodeEmitter::emitNameOp(). Here, at http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/frontend/BytecodeEmitter.cpp#l2272: if (!pn->pn_cookie.isFree()) { MOZ_ASSERT(JOF_OPTYPE(op) != JOF_ATOM); if (!emitVarOp(pn, op)) return false; } else { if (!emitAtomOp(pn, op)) return false; } ... despite |op| being equal to JSOP_GETALIASEDVAR, this code calls emitAtomOp() because the "if (!pn->pn_cookie.isFree()) {" check evaluates false. This is wrong - it should be calling emitVarOp(). Changing the MOZ_ASSERT at the top of js::frontend::BytecodeEmitter::emitAtomOp() to a MOZ_RELEASE_ASSERT verifies that we hit this. In gdb: (gdb) f 1 #1 0xf58099bf in emitAtomOp (op=<optimised out>, pn=0x825bc60, this=0xffffa408) at /home/chr1s/src/firefox/build-area/firefox-39.0+build5/js/src/frontend/BytecodeEmitter.cpp:1071 1071 return emitAtomOp(pn->pn_atom, op); (gdb) p pn->pn_u.name.cookie $2 = {level_ = 255, slot_ = 4, static FREE_LEVEL = 255} So, the correct branch is taken. Which means that |pn| is invalid. In fact, it goes wrong in js::frontend::BytecodeEmitter::tryConvertFreeName(), just here at http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/frontend/BytecodeEmitter.cpp#l1567: JSOp op; switch (pn->getOp()) { case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break; case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; default: return false; } pn->setOp(op); JS_ALWAYS_TRUE(pn->pn_cookie.set(parser->tokenStream, hops, slot)); return true; If I add a "MOZ_RELEASE_ASSERT(!pn->pn_cookie.isFree());" just before the "return true", then we hit this in our broken build. The line above calls js::frontend::UpvarCookie::set(), which is inlined from http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/frontend/ParseNode.h#l51: bool set(TokenStream& ts, unsigned newLevel, uint32_t newSlot) { if (newLevel >= FREE_LEVEL) return ts.reportError(JSMSG_TOO_DEEP, js_function_str); if (newSlot >= SCOPECOORD_SLOT_LIMIT) return ts.reportError(JSMSG_TOO_MANY_LOCALS); level_ = newLevel; slot_ = newSlot; return true; } ... which seems simple enough. Note that |level_| and |slot_| both fit in to 32-bits, with |slot_| taking the least-significant byte and |level_| taking the other 3 bytes. If I disassemble this bit of code in js::frontend::BytecodeEmitter::tryConvertFreeName() in a *good" build, starting at http://hg.mozilla.org/releases/mozilla- release/file/7665b8d4d51f/js/src/frontend/BytecodeEmitter.cpp#l1572: The starting context looks like this: %eax = |op| 0x1c(%esp) = |hops| 0x24(%esp) = |slot| 0x74(%esp) = |pn| 0xf4eb978b <+1739>: mov 0xa0(%ebp),%ecx 0xf4eb9791 <+1745>: mov 0x74(%esp),%edi // %edi now points to |pn| 0xf4eb9795 <+1749>: add $0x18,%ecx 0xf4eb9798 <+1752>: cmpl $0xfe,0x1c(%esp) // Compare |hops| with 254 (FREE_LEVEL - 1) 0xf4eb97a0 <+1760>: mov %al,0x2(%edi) // Calls pn->SetOp(op) 0xf4eb97a3 <+1763>: mov 0x24(%esp),%eax // %eax now contains |slot| // Jump if |hops| > 254 0xf4eb97a7 <+1767>: ja 0xf4eb97e9 <js::frontend::BytecodeEmitter::tryConvertFreeName(js::frontend::ParseNode*)+1833> 0xf4eb97a9 <+1769>: cmp $0xffffff,%eax // Compare |slot| with 0xffffff // Jump if |slot| > 0xffffff 0xf4eb97ae <+1774>: ja 0xf4eb980b <js::frontend::BytecodeEmitter::tryConvertFreeName(js::frontend::ParseNode*)+1867> 0xf4eb97b0 <+1776>: movzbl 0x1c(%esp),%ecx // %ecx now contains |hops| 0xf4eb97b5 <+1781>: mov 0x74(%esp),%edi // %edi now points to |pn| 0xf4eb97b9 <+1785>: shl $0x8,%eax // Left shift new |slot| value by 8-bits 0xf4eb97bc <+1788>: or %ecx,%eax // %eax now contains the bitwise-OR of |hops| and |slot| 0xf4eb97be <+1790>: mov %eax,0x20(%edi) // Save the new values to |level_| and |slot_| in pn->pn_u.name.cookie Now, looking at a broken build: The starting context looks like this: %eax = |op| 0x38(%esp) = |hops| 0x44(%esp) = |slot| 0x94(%esp) = |pn| 0xf5803ba1 <+1729>: mov 0xa0(%ebp),%edx 0xf5803ba7 <+1735>: mov 0x94(%esp),%esi // %esi now points to |pn| 0xf5803bae <+1742>: add $0x18,%edx 0xf5803bb1 <+1745>: cmpl $0xfe,0x38(%esp) // Compare |hops| with 254 (FREE_LEVEL - 1) 0xf5803bb9 <+1753>: mov %al,0x2(%esi) // Calls pn->SetOp(op) 0xf5803bbc <+1756>: mov 0x44(%esp),%eax // %eax now contains |slot| // Jump if |hops| > 254 0xf5803bc0 <+1760>: ja 0xf5803c22 <js::frontend::BytecodeEmitter::tryConvertFreeName(js::frontend::ParseNode*)+1858> 0xf5803bc2 <+1762>: cmp $0xffffff,%eax // Compare |slot| with 0xffffff // Jump if |slot| > 0xffffff 0xf5803bc7 <+1767>: ja 0xf5803c09 <js::frontend::BytecodeEmitter::tryConvertFreeName(js::frontend::ParseNode*)+1833> 0xf5803bc9 <+1769>: mov 0x94(%esp),%esi // %esi now points to |pn| 0xf5803bd0 <+1776>: shl $0x8,%eax // Left shift new |slot| value by 8-bits 0xf5803bd3 <+1779>: mov %eax,%edx // ...and copy it in to %edx // Now, this is where it goes wrong. The next instruction loads the current |level_| from pn->pn_u.name.cookie (which is 255) in to %eax 0xf5803bd5 <+1781>: movzbl 0x20(%esi),%eax 0xf5803bd9 <+1785>: or %edx,%eax // %eax now contains the bitwise-OR of the old |level_| and new |slot| value 0xf5803bdb <+1787>: mov %eax,0x20(%esi) // Save the new |slot_| So, this appears to be a miscompile in js::frontend::BytecodeEmitter::tryConvertFreeName - the inlined call to js::frontend::UpvarCookie::set() correctly sets |slot_| but always preserves the previous value of |level_|, causing the call to js::frontend::UpvarCookie::isFree() to return the wrong value in BytecodeEmitter::emitNameOp(). -- You received this bug notification because you are a member of Desktop Packages, which is subscribed to firefox in Ubuntu. https://bugs.launchpad.net/bugs/1471949 Title: Firefox 39 crashes on startup or within a few seconds on Precise/x86 Status in firefox package in Ubuntu: Invalid Status in firefox source package in Precise: Triaged Bug description: This is blocking publication of Firefox 39. The build for x86 on 12.04 currently crashes on startup, or within a few seconds of startup. It's basically unusable. An example crash report is: https://crash-stats.mozilla.com/report/index/d0d97dbb-f6bc- 4e4d-88ff-e5fff2150702. Unfortunately, despite the warning in the PPA description for https://launchpad.net/~ubuntu-mozilla-security/+archive/ubuntu/ppa, ordinary users are still installing pre-release packages from it. It works on all other releases and on Precise/x86-64. I did test Firefox 39 with this toolchain when it was still the nightly version whilst preparing the switch to GCC 4.8, and it worked fine. To manage notifications about this bug go to: https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1471949/+subscriptions -- Mailing list: https://launchpad.net/~desktop-packages Post to : desktop-packages@lists.launchpad.net Unsubscribe : https://launchpad.net/~desktop-packages More help : https://help.launchpad.net/ListHelp