Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | # Pretty-printers for SpiderMonkey jsvals. |
michael@0 | 2 | |
michael@0 | 3 | import gdb |
michael@0 | 4 | import gdb.types |
michael@0 | 5 | import mozilla.prettyprinters |
michael@0 | 6 | from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer |
michael@0 | 7 | |
michael@0 | 8 | # Forget any printers from previous loads of this module. |
michael@0 | 9 | mozilla.prettyprinters.clear_module_printers(__name__) |
michael@0 | 10 | |
michael@0 | 11 | # Summary of the JS::Value (also known as jsval) type: |
michael@0 | 12 | # |
michael@0 | 13 | # Viewed abstractly, JS::Value is a 64-bit discriminated union, with |
michael@0 | 14 | # JSString *, JSObject *, IEEE 64-bit floating-point, and 32-bit integer |
michael@0 | 15 | # branches (and a few others). (It is not actually a C++ union; |
michael@0 | 16 | # 'discriminated union' just describes the overall effect.) Note that |
michael@0 | 17 | # JS::Value is always 64 bits long, even on 32-bit architectures. |
michael@0 | 18 | # |
michael@0 | 19 | # The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit |
michael@0 | 20 | # floating-point values. A JS::Value can represent any JavaScript number |
michael@0 | 21 | # value directly, without referring to additional storage, or represent an |
michael@0 | 22 | # object, string, or other ECMAScript value, and remember which type it is. |
michael@0 | 23 | # This may seem surprising: how can a 64-bit type hold all the 64-bit IEEE |
michael@0 | 24 | # values, and still distinguish them from objects, strings, and so on, |
michael@0 | 25 | # which have 64-bit addresses? |
michael@0 | 26 | # |
michael@0 | 27 | # This is possible for two reasons: |
michael@0 | 28 | # |
michael@0 | 29 | # - First, ECMAScript implementations aren't required to distinguish all |
michael@0 | 30 | # the values the IEEE 64-bit format can represent. The IEEE format |
michael@0 | 31 | # specifies many bitstrings representing NaN values, while ECMAScript |
michael@0 | 32 | # requires only a single NaN value. This means we can use one IEEE NaN to |
michael@0 | 33 | # represent ECMAScript's NaN, and use all the other IEEE NaNs to |
michael@0 | 34 | # represent the other ECMAScript values. |
michael@0 | 35 | # |
michael@0 | 36 | # (IEEE says that any floating-point value whose 11-bit exponent field is |
michael@0 | 37 | # 0x7ff (all ones) and whose 52-bit fraction field is non-zero is a NaN. |
michael@0 | 38 | # So as long as we ensure the fraction field is non-zero, and save a NaN |
michael@0 | 39 | # for ECMAScript, we have 2^52 values to play with.) |
michael@0 | 40 | # |
michael@0 | 41 | # - Second, on the only 64-bit architecture we support, x86_64, only the |
michael@0 | 42 | # lower 48 bits of an address are significant. The upper sixteen bits are |
michael@0 | 43 | # required to be the sign-extension of bit 48. Furthermore, user code |
michael@0 | 44 | # always runs in "positive addresses": those in which bit 48 is zero. So |
michael@0 | 45 | # we only actually need 47 bits to store all possible object or string |
michael@0 | 46 | # addresses, even on 64-bit platforms. |
michael@0 | 47 | # |
michael@0 | 48 | # With a 52-bit fraction field, and 47 bits needed for the 'payload', we |
michael@0 | 49 | # have up to five bits left to store a 'tag' value, to indicate which |
michael@0 | 50 | # branch of our discriminated union is live. |
michael@0 | 51 | # |
michael@0 | 52 | # Thus, we define JS::Value representations in terms of the IEEE 64-bit |
michael@0 | 53 | # floating-point format: |
michael@0 | 54 | # |
michael@0 | 55 | # - Any bitstring that IEEE calls a number or an infinity represents that |
michael@0 | 56 | # ECMAScript number. |
michael@0 | 57 | # |
michael@0 | 58 | # - Any bitstring that IEEE calls a NaN represents either an ECMAScript NaN |
michael@0 | 59 | # or a non-number ECMAScript value, as determined by a tag field stored |
michael@0 | 60 | # towards the most significant end of the fraction field (exactly where |
michael@0 | 61 | # depends on the address size). If the tag field indicates that this |
michael@0 | 62 | # JS::Value is an object, the fraction field's least significant end |
michael@0 | 63 | # holds the address of a JSObject; if a string, the address of a |
michael@0 | 64 | # JSString; and so on. |
michael@0 | 65 | # |
michael@0 | 66 | # On the only 64-bit platform we support, x86_64, only the lower 48 bits of |
michael@0 | 67 | # an address are significant, and only those values whose top bit is zero |
michael@0 | 68 | # are used for user-space addresses. This means that x86_64 addresses are |
michael@0 | 69 | # effectively 47 bits long, and thus fit nicely in the available portion of |
michael@0 | 70 | # the fraction field. |
michael@0 | 71 | # |
michael@0 | 72 | # |
michael@0 | 73 | # In detail: |
michael@0 | 74 | # |
michael@0 | 75 | # - jsval (Value.h) is a typedef for JS::Value. |
michael@0 | 76 | # |
michael@0 | 77 | # - JS::Value (Value.h) is a class with a lot of methods and a single data |
michael@0 | 78 | # member, of type jsval_layout. |
michael@0 | 79 | # |
michael@0 | 80 | # - jsval_layout (Value.h) is a helper type for picking apart values. This |
michael@0 | 81 | # is always 64 bits long, with a variant for each address size (32 bits |
michael@0 | 82 | # or 64 bits) and endianness (little- or big-endian). |
michael@0 | 83 | # |
michael@0 | 84 | # jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr' |
michael@0 | 85 | # branches, and an 's' branch, which is a struct that tries to break out |
michael@0 | 86 | # the bitfields a little for the non-double types. On 64-bit machines, |
michael@0 | 87 | # jsval_layout also has an 'asUIntPtr' branch. |
michael@0 | 88 | # |
michael@0 | 89 | # On 32-bit platforms, the 's' structure has a 'tag' member at the |
michael@0 | 90 | # exponent end of the 's' struct, and a 'payload' union at the mantissa |
michael@0 | 91 | # end. The 'payload' union's branches are things like JSString *, |
michael@0 | 92 | # JSObject *, and so on: the natural representations of the tags. |
michael@0 | 93 | # |
michael@0 | 94 | # On 64-bit platforms, the payload is 47 bits long; since C++ doesn't let |
michael@0 | 95 | # us declare bitfields that hold unions, we can't break it down so |
michael@0 | 96 | # neatly. In this case, we apply bit-shifting tricks to the 'asBits' |
michael@0 | 97 | # branch of the union to extract the tag. |
michael@0 | 98 | |
michael@0 | 99 | class Box(object): |
michael@0 | 100 | def __init__(self, asBits, jtc): |
michael@0 | 101 | self.asBits = asBits |
michael@0 | 102 | self.jtc = jtc |
michael@0 | 103 | # jsval_layout::asBits is uint64, but somebody botches the sign bit, even |
michael@0 | 104 | # though Python integers are arbitrary precision. |
michael@0 | 105 | if self.asBits < 0: |
michael@0 | 106 | self.asBits = self.asBits + (1 << 64) |
michael@0 | 107 | |
michael@0 | 108 | # Return this value's type tag. |
michael@0 | 109 | def tag(self): raise NotImplementedError |
michael@0 | 110 | |
michael@0 | 111 | # Return this value as a 32-bit integer, double, or address. |
michael@0 | 112 | def as_uint32(self): raise NotImplementedError |
michael@0 | 113 | def as_double(self): raise NotImplementedError |
michael@0 | 114 | def as_address(self): raise NotImplementedError |
michael@0 | 115 | |
michael@0 | 116 | # Packed non-number boxing --- the format used on x86_64. It would be nice to simply |
michael@0 | 117 | # call JSVAL_TO_INT, etc. here, but the debugger is likely to see many jsvals, and |
michael@0 | 118 | # doing several inferior calls for each one seems like a bad idea. |
michael@0 | 119 | class Punbox(Box): |
michael@0 | 120 | |
michael@0 | 121 | FULL_WIDTH = 64 |
michael@0 | 122 | TAG_SHIFT = 47 |
michael@0 | 123 | PAYLOAD_MASK = (1 << TAG_SHIFT) - 1 |
michael@0 | 124 | TAG_MASK = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1 |
michael@0 | 125 | TAG_MAX_DOUBLE = 0x1fff0 |
michael@0 | 126 | TAG_TYPE_MASK = 0x0000f |
michael@0 | 127 | |
michael@0 | 128 | def tag(self): |
michael@0 | 129 | tag = self.asBits >> Punbox.TAG_SHIFT |
michael@0 | 130 | if tag <= Punbox.TAG_MAX_DOUBLE: |
michael@0 | 131 | return self.jtc.DOUBLE |
michael@0 | 132 | else: |
michael@0 | 133 | return tag & Punbox.TAG_TYPE_MASK |
michael@0 | 134 | |
michael@0 | 135 | def as_uint32(self): return int(self.asBits & ((1 << 32) - 1)) |
michael@0 | 136 | def as_address(self): return gdb.Value(self.asBits & Punbox.PAYLOAD_MASK) |
michael@0 | 137 | |
michael@0 | 138 | class Nunbox(Box): |
michael@0 | 139 | TAG_SHIFT = 32 |
michael@0 | 140 | TAG_CLEAR = 0xffff0000 |
michael@0 | 141 | PAYLOAD_MASK = 0xffffffff |
michael@0 | 142 | TAG_TYPE_MASK = 0x0000000f |
michael@0 | 143 | |
michael@0 | 144 | def tag(self): |
michael@0 | 145 | tag = self.asBits >> Nunbox.TAG_SHIFT |
michael@0 | 146 | if tag < Nunbox.TAG_CLEAR: |
michael@0 | 147 | return self.jtc.DOUBLE |
michael@0 | 148 | return tag & Nunbox.TAG_TYPE_MASK |
michael@0 | 149 | |
michael@0 | 150 | def as_uint32(self): return int(self.asBits & Nunbox.PAYLOAD_MASK) |
michael@0 | 151 | def as_address(self): return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK) |
michael@0 | 152 | |
michael@0 | 153 | # Cache information about the jsval type for this objfile. |
michael@0 | 154 | class jsvalTypeCache(object): |
michael@0 | 155 | def __init__(self, cache): |
michael@0 | 156 | # Capture the tag values. |
michael@0 | 157 | d = gdb.types.make_enum_dict(gdb.lookup_type('JSValueType')) |
michael@0 | 158 | self.DOUBLE = d['JSVAL_TYPE_DOUBLE'] |
michael@0 | 159 | self.INT32 = d['JSVAL_TYPE_INT32'] |
michael@0 | 160 | self.UNDEFINED = d['JSVAL_TYPE_UNDEFINED'] |
michael@0 | 161 | self.BOOLEAN = d['JSVAL_TYPE_BOOLEAN'] |
michael@0 | 162 | self.MAGIC = d['JSVAL_TYPE_MAGIC'] |
michael@0 | 163 | self.STRING = d['JSVAL_TYPE_STRING'] |
michael@0 | 164 | self.NULL = d['JSVAL_TYPE_NULL'] |
michael@0 | 165 | self.OBJECT = d['JSVAL_TYPE_OBJECT'] |
michael@0 | 166 | |
michael@0 | 167 | # Let self.magic_names be an array whose i'th element is the name of |
michael@0 | 168 | # the i'th magic value. |
michael@0 | 169 | d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic')) |
michael@0 | 170 | self.magic_names = range(max(d.itervalues()) + 1) |
michael@0 | 171 | for (k,v) in d.items(): self.magic_names[v] = k |
michael@0 | 172 | |
michael@0 | 173 | # Choose an unboxing scheme for this architecture. |
michael@0 | 174 | self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox |
michael@0 | 175 | |
michael@0 | 176 | @pretty_printer('jsval_layout') |
michael@0 | 177 | class jsval_layout(object): |
michael@0 | 178 | def __init__(self, value, cache): |
michael@0 | 179 | # Save the generic typecache, and create our own, if we haven't already. |
michael@0 | 180 | self.cache = cache |
michael@0 | 181 | if not cache.mod_jsval: |
michael@0 | 182 | cache.mod_jsval = jsvalTypeCache(cache) |
michael@0 | 183 | self.jtc = cache.mod_jsval |
michael@0 | 184 | |
michael@0 | 185 | self.value = value |
michael@0 | 186 | self.box = self.jtc.boxer(value['asBits'], self.jtc) |
michael@0 | 187 | |
michael@0 | 188 | def to_string(self): |
michael@0 | 189 | tag = self.box.tag() |
michael@0 | 190 | if tag == self.jtc.INT32: |
michael@0 | 191 | value = self.box.as_uint32() |
michael@0 | 192 | signbit = 1 << 31 |
michael@0 | 193 | value = (value ^ signbit) - signbit |
michael@0 | 194 | elif tag == self.jtc.UNDEFINED: |
michael@0 | 195 | return 'JSVAL_VOID' |
michael@0 | 196 | elif tag == self.jtc.BOOLEAN: |
michael@0 | 197 | return 'JSVAL_TRUE' if self.box.as_uint32() else 'JSVAL_FALSE' |
michael@0 | 198 | elif tag == self.jtc.MAGIC: |
michael@0 | 199 | value = self.box.as_uint32() |
michael@0 | 200 | if 0 <= value and value < len(self.jtc.magic_names): |
michael@0 | 201 | return '$jsmagic(%s)' % (self.jtc.magic_names[value],) |
michael@0 | 202 | else: |
michael@0 | 203 | return '$jsmagic(%d)' % (value,) |
michael@0 | 204 | elif tag == self.jtc.STRING: |
michael@0 | 205 | value = self.box.as_address().cast(self.cache.JSString_ptr_t) |
michael@0 | 206 | elif tag == self.jtc.NULL: |
michael@0 | 207 | return 'JSVAL_NULL' |
michael@0 | 208 | elif tag == self.jtc.OBJECT: |
michael@0 | 209 | value = self.box.as_address().cast(self.cache.JSObject_ptr_t) |
michael@0 | 210 | elif tag == self.jtc.DOUBLE: |
michael@0 | 211 | value = self.value['asDouble'] |
michael@0 | 212 | else: |
michael@0 | 213 | return '$jsval(unrecognized!)' |
michael@0 | 214 | return '$jsval(%s)' % (value,) |
michael@0 | 215 | |
michael@0 | 216 | @pretty_printer('JS::Value') |
michael@0 | 217 | class JSValue(object): |
michael@0 | 218 | def __new__(cls, value, cache): |
michael@0 | 219 | return jsval_layout(value['data'], cache) |