js/public/Anchor.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* JS::Anchor implementation. */
michael@0 8
michael@0 9 #ifndef js_Anchor_h
michael@0 10 #define js_Anchor_h
michael@0 11
michael@0 12 #include "mozilla/Attributes.h"
michael@0 13
michael@0 14 #include "js/TypeDecls.h"
michael@0 15
michael@0 16 namespace JS {
michael@0 17
michael@0 18 /*
michael@0 19 * Protecting non-Value, non-JSObject *, non-JSString * values from collection
michael@0 20 *
michael@0 21 * Most of the time, the garbage collector's conservative stack scanner works
michael@0 22 * behind the scenes, finding all live values and protecting them from being
michael@0 23 * collected. However, when JSAPI client code obtains a pointer to data the
michael@0 24 * scanner does not know about, owned by an object the scanner does know about,
michael@0 25 * Care Must Be Taken.
michael@0 26 *
michael@0 27 * The scanner recognizes only a select set of types: pointers to JSObjects and
michael@0 28 * similar things (JSFunctions, and so on), pointers to JSStrings, and Values.
michael@0 29 * So while the scanner finds all live |JSString| pointers, it does not notice
michael@0 30 * |jschar| pointers.
michael@0 31 *
michael@0 32 * So suppose we have:
michael@0 33 *
michael@0 34 * void f(JSString *str) {
michael@0 35 * const jschar *ch = JS_GetStringCharsZ(str);
michael@0 36 * ... do stuff with ch, but no uses of str ...;
michael@0 37 * }
michael@0 38 *
michael@0 39 * After the call to |JS_GetStringCharsZ|, there are no further uses of
michael@0 40 * |str|, which means that the compiler is within its rights to not store
michael@0 41 * it anywhere. But because the stack scanner will not notice |ch|, there
michael@0 42 * is no longer any live value in this frame that would keep the string
michael@0 43 * alive. If |str| is the last reference to that |JSString|, and the
michael@0 44 * collector runs while we are using |ch|, the string's array of |jschar|s
michael@0 45 * may be freed out from under us.
michael@0 46 *
michael@0 47 * Note that there is only an issue when 1) we extract a thing X the scanner
michael@0 48 * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y
michael@0 49 * gets garbage-collected, then X gets freed. If we have code like this:
michael@0 50 *
michael@0 51 * void g(JSObject *obj) {
michael@0 52 * JS::Value x;
michael@0 53 * JS_GetProperty(obj, "x", &x);
michael@0 54 * ... do stuff with x ...
michael@0 55 * }
michael@0 56 *
michael@0 57 * there's no problem, because the value we've extracted, x, is a Value, a
michael@0 58 * type that the conservative scanner recognizes.
michael@0 59 *
michael@0 60 * Conservative GC frees us from the obligation to explicitly root the types it
michael@0 61 * knows about, but when we work with derived values like |ch|, we must root
michael@0 62 * their owners, as the derived value alone won't keep them alive.
michael@0 63 *
michael@0 64 * A JS::Anchor is a kind of GC root that allows us to keep the owners of
michael@0 65 * derived values like |ch| alive throughout the Anchor's lifetime. We could
michael@0 66 * fix the above code as follows:
michael@0 67 *
michael@0 68 * void f(JSString *str) {
michael@0 69 * JS::Anchor<JSString *> a_str(str);
michael@0 70 * const jschar *ch = JS_GetStringCharsZ(str);
michael@0 71 * ... do stuff with ch, but no uses of str ...;
michael@0 72 * }
michael@0 73 *
michael@0 74 * This simply ensures that |str| will be live until |a_str| goes out of scope.
michael@0 75 * As long as we don't retain a pointer to the string's characters for longer
michael@0 76 * than that, we have avoided all garbage collection hazards.
michael@0 77 */
michael@0 78 template<typename T> class AnchorPermitted;
michael@0 79 template<> class AnchorPermitted<JSObject *> { };
michael@0 80 template<> class AnchorPermitted<const JSObject *> { };
michael@0 81 template<> class AnchorPermitted<JSFunction *> { };
michael@0 82 template<> class AnchorPermitted<const JSFunction *> { };
michael@0 83 template<> class AnchorPermitted<JSString *> { };
michael@0 84 template<> class AnchorPermitted<const JSString *> { };
michael@0 85 template<> class AnchorPermitted<Value> { };
michael@0 86 template<> class AnchorPermitted<const JSScript *> { };
michael@0 87 template<> class AnchorPermitted<JSScript *> { };
michael@0 88
michael@0 89 template<typename T>
michael@0 90 class Anchor : AnchorPermitted<T>
michael@0 91 {
michael@0 92 public:
michael@0 93 Anchor() { }
michael@0 94 explicit Anchor(T t) { hold = t; }
michael@0 95 inline ~Anchor();
michael@0 96
michael@0 97 private:
michael@0 98 T hold;
michael@0 99
michael@0 100 /*
michael@0 101 * Rooting analysis considers use of operator= to be a use of an anchor.
michael@0 102 * For simplicity, Anchor is treated as if it contained a GC thing, from
michael@0 103 * construction. Thus if we had
michael@0 104 *
michael@0 105 * void operator=(const T &t) { hold = t; }
michael@0 106 *
michael@0 107 * and this code
michael@0 108 *
michael@0 109 * JS::Anchor<JSString*> anchor;
michael@0 110 * stuff that could GC, producing |str|;
michael@0 111 * anchor = str;
michael@0 112 *
michael@0 113 * the last line would be seen as a hazard, because the final = would "use"
michael@0 114 * |anchor| that is a GC thing -- which could have been moved around by the
michael@0 115 * GC. The workaround is to structure your code so that JS::Anchor is
michael@0 116 * always constructed, living for however long the corresponding value must
michael@0 117 * live.
michael@0 118 */
michael@0 119 void operator=(const T &t) MOZ_DELETE;
michael@0 120
michael@0 121 Anchor(const Anchor &other) MOZ_DELETE;
michael@0 122 void operator=(const Anchor &other) MOZ_DELETE;
michael@0 123 };
michael@0 124
michael@0 125 template<typename T>
michael@0 126 inline Anchor<T>::~Anchor()
michael@0 127 {
michael@0 128 #ifdef __GNUC__
michael@0 129 /*
michael@0 130 * No code is generated for this. But because this is marked 'volatile', G++ will
michael@0 131 * assume it has important side-effects, and won't delete it. (G++ never looks at
michael@0 132 * the actual text and notices it's empty.) And because we have passed |hold| to
michael@0 133 * it, GCC will keep |hold| alive until this point.
michael@0 134 *
michael@0 135 * The "memory" clobber operand ensures that G++ will not move prior memory
michael@0 136 * accesses after the asm --- it's a barrier. Unfortunately, it also means that
michael@0 137 * G++ will assume that all memory has changed after the asm, as it would for a
michael@0 138 * call to an unknown function. I don't know of a way to avoid that consequence.
michael@0 139 */
michael@0 140 asm volatile("":: "g" (hold) : "memory");
michael@0 141 #else
michael@0 142 /*
michael@0 143 * An adequate portable substitute, for non-structure types.
michael@0 144 *
michael@0 145 * The compiler promises that, by the end of an expression statement, the
michael@0 146 * last-stored value to a volatile object is the same as it would be in an
michael@0 147 * unoptimized, direct implementation (the "abstract machine" whose behavior the
michael@0 148 * language spec describes). However, the compiler is still free to reorder
michael@0 149 * non-volatile accesses across this store --- which is what we must prevent. So
michael@0 150 * assigning the held value to a volatile variable, as we do here, is not enough.
michael@0 151 *
michael@0 152 * In our case, however, garbage collection only occurs at function calls, so it
michael@0 153 * is sufficient to ensure that the destructor's store isn't moved earlier across
michael@0 154 * any function calls that could collect. It is hard to imagine the compiler
michael@0 155 * analyzing the program so thoroughly that it could prove that such motion was
michael@0 156 * safe. In practice, compilers treat calls to the collector as opaque operations
michael@0 157 * --- in particular, as operations which could access volatile variables, across
michael@0 158 * which this destructor must not be moved.
michael@0 159 *
michael@0 160 * ("Objection, your honor! *Alleged* killer whale!")
michael@0 161 *
michael@0 162 * The disadvantage of this approach is that it does generate code for the store.
michael@0 163 * We do need to use Anchors in some cases where cycles are tight.
michael@0 164 *
michael@0 165 * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h.
michael@0 166 */
michael@0 167 volatile T sink;
michael@0 168 sink = hold;
michael@0 169 #endif /* defined(__GNUC__) */
michael@0 170 }
michael@0 171
michael@0 172 } // namespace JS
michael@0 173
michael@0 174 #endif /* js_Anchor_h */

mercurial