1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/scripting/mozjs/engine.h"
36 
37 #include <js/Initialization.h>
38 
39 #include "mongo/db/operation_context.h"
40 #include "mongo/db/server_parameters.h"
41 #include "mongo/scripting/mozjs/implscope.h"
42 #include "mongo/scripting/mozjs/proxyscope.h"
43 #include "mongo/util/log.h"
44 
45 namespace js {
46 void DisableExtraThreads();
47 }
48 
49 namespace mongo {
50 
51 namespace {
52 
53 MONGO_EXPORT_SERVER_PARAMETER(disableJavaScriptJIT, bool, false);
54 MONGO_EXPORT_SERVER_PARAMETER(javascriptProtection, bool, false);
55 MONGO_EXPORT_SERVER_PARAMETER(jsHeapLimitMB, int, 1100);
56 
57 }  // namespace
58 
setup()59 void ScriptEngine::setup() {
60     if (getGlobalScriptEngine())
61         return;
62 
63     setGlobalScriptEngine(new mozjs::MozJSScriptEngine());
64 
65     if (hasGlobalServiceContext()) {
66         getGlobalServiceContext()->registerKillOpListener(getGlobalScriptEngine());
67     }
68 }
69 
getInterpreterVersionString()70 std::string ScriptEngine::getInterpreterVersionString() {
71     return "MozJS-38";
72 }
73 
74 namespace mozjs {
75 
MozJSScriptEngine()76 MozJSScriptEngine::MozJSScriptEngine() {
77     uassert(ErrorCodes::JSInterpreterFailure, "Failed to JS_Init()", JS_Init());
78     js::DisableExtraThreads();
79 }
80 
~MozJSScriptEngine()81 MozJSScriptEngine::~MozJSScriptEngine() {
82     JS_ShutDown();
83 }
84 
createScope()85 mongo::Scope* MozJSScriptEngine::createScope() {
86     return new MozJSProxyScope(this);
87 }
88 
createScopeForCurrentThread()89 mongo::Scope* MozJSScriptEngine::createScopeForCurrentThread() {
90     return new MozJSImplScope(this);
91 }
92 
interrupt(unsigned opId)93 void MozJSScriptEngine::interrupt(unsigned opId) {
94     stdx::lock_guard<stdx::mutex> intLock(_globalInterruptLock);
95     OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId);
96     if (iScope == _opToScopeMap.end()) {
97         // got interrupt request for a scope that no longer exists
98         LOG(1) << "received interrupt request for unknown op: " << opId << printKnownOps_inlock();
99         return;
100     }
101 
102     LOG(1) << "interrupting op: " << opId << printKnownOps_inlock();
103     iScope->second->kill();
104 }
105 
printKnownOps_inlock()106 std::string MozJSScriptEngine::printKnownOps_inlock() {
107     str::stream out;
108 
109     if (shouldLog(logger::LogSeverity::Debug(2))) {
110         out << "  known ops: \n";
111 
112         for (auto&& iSc : _opToScopeMap) {
113             out << "  " << iSc.first << "\n";
114         }
115     }
116 
117     return out;
118 }
119 
interruptAll()120 void MozJSScriptEngine::interruptAll() {
121     stdx::lock_guard<stdx::mutex> interruptLock(_globalInterruptLock);
122 
123     for (auto&& iScope : _opToScopeMap) {
124         iScope.second->kill();
125     }
126 }
127 
enableJIT(bool value)128 void MozJSScriptEngine::enableJIT(bool value) {
129     disableJavaScriptJIT.store(!value);
130 }
131 
isJITEnabled() const132 bool MozJSScriptEngine::isJITEnabled() const {
133     return !disableJavaScriptJIT.load();
134 }
135 
enableJavaScriptProtection(bool value)136 void MozJSScriptEngine::enableJavaScriptProtection(bool value) {
137     javascriptProtection.store(value);
138 }
139 
isJavaScriptProtectionEnabled() const140 bool MozJSScriptEngine::isJavaScriptProtectionEnabled() const {
141     return javascriptProtection.load();
142 }
143 
getJSHeapLimitMB() const144 int MozJSScriptEngine::getJSHeapLimitMB() const {
145     return jsHeapLimitMB.load();
146 }
147 
setJSHeapLimitMB(int limit)148 void MozJSScriptEngine::setJSHeapLimitMB(int limit) {
149     jsHeapLimitMB.store(limit);
150 }
151 
registerOperation(OperationContext * opCtx,MozJSImplScope * scope)152 void MozJSScriptEngine::registerOperation(OperationContext* opCtx, MozJSImplScope* scope) {
153     stdx::lock_guard<stdx::mutex> giLock(_globalInterruptLock);
154 
155     auto opId = opCtx->getOpID();
156 
157     _opToScopeMap[opId] = scope;
158 
159     LOG(2) << "SMScope " << static_cast<const void*>(scope) << " registered for op " << opId;
160     Status status = opCtx->checkForInterruptNoAssert();
161     if (!status.isOK()) {
162         scope->kill();
163     }
164 }
165 
unregisterOperation(unsigned int opId)166 void MozJSScriptEngine::unregisterOperation(unsigned int opId) {
167     stdx::lock_guard<stdx::mutex> giLock(_globalInterruptLock);
168 
169     LOG(2) << "ImplScope " << static_cast<const void*>(this) << " unregistered for op " << opId;
170 
171     if (opId != 0) {
172         // scope is currently associated with an operation id
173         auto it = _opToScopeMap.find(opId);
174         if (it != _opToScopeMap.end())
175             _opToScopeMap.erase(it);
176     }
177 }
178 
179 }  // namespace mozjs
180 }  // namespace mongo
181