Skip to content

Commit cce1653

Browse files
committed
[lldb] Fix use-after-free in Playgrounds
The precise compiler invocations change moved the ownership of the SwiftPersistentExpressionState from SwiftASTContext to TypeSystemSwiftTypeRef, which makes it shared between all SwiftASTCOntextForExpression objects. The LLDB name lookup contained a check to avoid finding a persistent result from the wrong context, but there was no such check in the REPL name lookup. This is a problem when a Playground imports a framework that pulls in a fresh dylib because this forces a new SwiftASTContext to be created. Duplicating the check avoids the crash. I further made sure to clear the persistent state when the SwiftASTContext is replaced, and added several assertions to ensure consistency. rdar://143923367 (cherry picked from commit c64b06a)
1 parent 3da4049 commit cce1653

File tree

12 files changed

+208
-22
lines changed

12 files changed

+208
-22
lines changed

lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,7 @@ LLDBNameLookup::LLDBNameLookup(
139139
SwiftExpressionParser::SILVariableMap &variable_map, SymbolContext &sc,
140140
ExecutionContextScope &exe_scope)
141141
: SILDebuggerClient(source_file.getASTContext()),
142-
m_log(GetLog(LLDBLog::Expressions)), m_source_file(source_file),
143-
m_variable_map(variable_map), m_sc(sc) {
142+
m_source_file(source_file), m_variable_map(variable_map), m_sc(sc) {
144143
source_file.getParentModule()->setDebugClient(this);
145144

146145
if (!m_sc.target_sp)
@@ -178,11 +177,15 @@ void LLDBNameLookup::RegisterTypeAliases(
178177

179178
/// A name lookup class for debugger expr mode.
180179
class LLDBExprNameLookup : public LLDBNameLookup {
180+
const SwiftASTContextForExpressions *m_this_context;
181+
181182
public:
182183
LLDBExprNameLookup(swift::SourceFile &source_file,
183184
SwiftExpressionParser::SILVariableMap &variable_map,
184-
SymbolContext &sc, ExecutionContextScope &exe_scope)
185-
: LLDBNameLookup(source_file, variable_map, sc, exe_scope) {}
185+
SymbolContext &sc, ExecutionContextScope &exe_scope,
186+
const SwiftASTContextForExpressions *this_context)
187+
: LLDBNameLookup(source_file, variable_map, sc, exe_scope),
188+
m_this_context(this_context) {}
186189

187190
bool shouldGlobalize(swift::Identifier Name, swift::DeclKind Kind) override {
188191
// Extensions have to be globalized, there's no way to mark them
@@ -196,7 +199,7 @@ class LLDBExprNameLookup : public LLDBNameLookup {
196199
return true;
197200

198201
if (Name.str().starts_with("$")) {
199-
LLDB_LOG(m_log,
202+
LLDB_LOG(GetLog(LLDBLog::Expressions),
200203
"[LLDBExprNameLookup::shouldGlobalize] Returning true to "
201204
"globalizing {0}",
202205
Name.str());
@@ -224,7 +227,7 @@ class LLDBExprNameLookup : public LLDBNameLookup {
224227
static unsigned counter = 0;
225228
unsigned count = counter++;
226229

227-
LLDB_LOG(m_log,
230+
LLDB_LOG(GetLog(LLDBLog::Expressions),
228231
"[LLDBExprNameLookup::lookupOverrides({0})] Searching for \"{1}\"",
229232
count, Name.getIdentifier().get());
230233

@@ -235,6 +238,8 @@ class LLDBExprNameLookup : public LLDBNameLookup {
235238
swift::SourceLoc Loc, bool IsTypeLookup,
236239
ResultVector &RV) override {
237240
LLDB_SCOPED_TIMER();
241+
assert(SwiftASTContext::GetSwiftASTContext(&DC->getASTContext()) ==
242+
m_this_context);
238243
static unsigned counter = 0;
239244
unsigned count = counter++;
240245

@@ -243,7 +248,7 @@ class LLDBExprNameLookup : public LLDBNameLookup {
243248
return false;
244249

245250
LLDB_LOG(
246-
m_log,
251+
GetLog(LLDBLog::Expressions),
247252
"[LLDBExprNameLookup::lookupAdditions ({0})] Searching for \"{1}\"",
248253
count, NameStr);
249254

@@ -291,9 +296,10 @@ class LLDBExprNameLookup : public LLDBNameLookup {
291296
persistent_results);
292297

293298
for (CompilerDecl & decl : persistent_results) {
294-
if (decl.GetTypeSystem() !=
295-
SwiftASTContext::GetSwiftASTContext(&DC->getASTContext())) {
296-
LLDB_LOG(m_log, "ignoring persistent result from other context");
299+
if (decl.GetTypeSystem() != m_this_context) {
300+
LLDB_LOG(GetLog(LLDBLog::Expressions),
301+
"Ignoring persistent result from other context: {0}",
302+
NameStr);
297303
continue;
298304
}
299305
auto *value_decl =
@@ -369,11 +375,15 @@ class LLDBExprNameLookup : public LLDBNameLookup {
369375

370376
/// A name lookup class for REPL and Playground mode.
371377
class LLDBREPLNameLookup : public LLDBNameLookup {
378+
const SwiftASTContextForExpressions *m_this_context;
379+
372380
public:
373381
LLDBREPLNameLookup(swift::SourceFile &source_file,
374382
SwiftExpressionParser::SILVariableMap &variable_map,
375-
SymbolContext &sc, ExecutionContextScope &exe_scope)
376-
: LLDBNameLookup(source_file, variable_map, sc, exe_scope) {}
383+
SymbolContext &sc, ExecutionContextScope &exe_scope,
384+
const SwiftASTContextForExpressions *this_context)
385+
: LLDBNameLookup(source_file, variable_map, sc, exe_scope),
386+
m_this_context(this_context) {}
377387

378388
bool shouldGlobalize(swift::Identifier Name, swift::DeclKind kind) override {
379389
return false;
@@ -391,6 +401,9 @@ class LLDBREPLNameLookup : public LLDBNameLookup {
391401
swift::SourceLoc Loc, bool IsTypeLookup,
392402
ResultVector &RV) override {
393403
LLDB_SCOPED_TIMER();
404+
405+
assert(SwiftASTContext::GetSwiftASTContext(&DC->getASTContext()) ==
406+
m_this_context);
394407
static unsigned counter = 0;
395408
unsigned count = counter++;
396409

@@ -399,7 +412,7 @@ class LLDBREPLNameLookup : public LLDBNameLookup {
399412
return false;
400413

401414
LLDB_LOG(
402-
m_log,
415+
GetLog(LLDBLog::Expressions),
403416
"[LLDBREPLNameLookup::lookupAdditions ({0})] Searching for \"{1}\"",
404417
count, NameStr);
405418

@@ -421,6 +434,12 @@ class LLDBREPLNameLookup : public LLDBNameLookup {
421434

422435
// Append the persistent decls that we found to the result vector.
423436
for (auto result : persistent_decl_results) {
437+
if (result.GetTypeSystem() != m_this_context) {
438+
LLDB_LOG(GetLog(LLDBLog::Expressions),
439+
"Ignoring persistent result from other context: {0}", NameStr);
440+
continue;
441+
}
442+
424443
// No import required.
425444
auto *result_decl =
426445
static_cast<swift::ValueDecl *>(result.GetOpaqueDecl());
@@ -1380,11 +1399,11 @@ SwiftExpressionParser::ParseAndImport(
13801399

13811400
LLDBNameLookup *external_lookup;
13821401
if (m_options.GetPlaygroundTransformEnabled() || m_options.GetREPLEnabled()) {
1383-
external_lookup =
1384-
new LLDBREPLNameLookup(*source_file, variable_map, m_sc, *m_exe_scope);
1402+
external_lookup = new LLDBREPLNameLookup(*source_file, variable_map, m_sc,
1403+
*m_exe_scope, &m_swift_ast_ctx);
13851404
} else {
1386-
external_lookup =
1387-
new LLDBExprNameLookup(*source_file, variable_map, m_sc, *m_exe_scope);
1405+
external_lookup = new LLDBExprNameLookup(*source_file, variable_map, m_sc,
1406+
*m_exe_scope, &m_swift_ast_ctx);
13881407
}
13891408

13901409
// FIXME: This call is here just so that the we keep the
@@ -1769,8 +1788,15 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
17691788

17701789
// Signal that we want to retry the expression exactly once with a
17711790
// fresh SwiftASTContext.
1772-
if (retry)
1791+
if (retry) {
1792+
if (auto *persistent_state =
1793+
llvm::cast_or_null<SwiftPersistentExpressionState>(
1794+
m_sc.target_sp->GetPersistentExpressionStateForLanguage(
1795+
lldb::eLanguageTypeSwift)))
1796+
persistent_state->Clear();
1797+
17731798
return ParseResult::retry_fresh_context;
1799+
}
17741800

17751801
// Unrecoverable error.
17761802
return parse_result_failure;
@@ -2033,6 +2059,11 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
20332059
return parse_result_failure;
20342060
}
20352061

2062+
if (m_swift_ast_ctx.GetASTContext()->hadError()) {
2063+
DiagnoseSwiftASTContextError();
2064+
return ParseResult::unrecoverable_error;
2065+
}
2066+
20362067
{
20372068
std::lock_guard<std::recursive_mutex> global_context_locker(
20382069
IRExecutionUnit::GetLLVMGlobalContextMutex());

lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class SwiftExpressionParser : public ExpressionParser {
215215
/// The container for the IR, to be JIT-compiled or interpreted.
216216
lldb::IRExecutionUnitSP m_execution_unit_sp;
217217
/// The AST context to build the expression into.
218+
/// A shared pointer of this is held by SwiftUserexpression.
218219
SwiftASTContextForExpressions &m_swift_ast_ctx;
219220
/// Used to manage the memory of a potential on-off context.
220221
//lldb::TypeSystemSP m_typesystem_sp;
@@ -257,7 +258,6 @@ class LLDBNameLookup : public swift::SILDebuggerClient {
257258
llvm::SmallVectorImpl<swift::TypeAliasDecl *> &type_aliases);
258259

259260
protected:
260-
Log *m_log;
261261
swift::SourceFile &m_source_file;
262262
SwiftExpressionParser::SILVariableMap &m_variable_map;
263263
SymbolContext m_sc;

lldb/source/Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class SwiftPersistentExpressionState : public PersistentExpressionState {
4848

4949
void CopyDeclsTo(SwiftDeclMap &target_map);
5050
static bool DeclsAreEquivalent(CompilerDecl lhs, CompilerDecl rhs);
51+
void Clear() { m_swift_decls.clear(); }
5152

5253
private:
5354
/// Each decl also stores the context it comes from.
@@ -102,6 +103,8 @@ class SwiftPersistentExpressionState : public PersistentExpressionState {
102103
const std::vector<CompilerDecl> &excluding_equivalents,
103104
std::vector<CompilerDecl> &matches);
104105

106+
void Clear() { m_swift_persistent_decls.Clear(); }
107+
105108
private:
106109
/// The counter used by GetNextResultName().
107110
uint32_t m_next_persistent_variable_id;

lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,9 +1104,6 @@ class SwiftASTContextForExpressions : public SwiftASTContext {
11041104
/// These are the names of modules that we have loaded by hand into
11051105
/// the Contexts we make for parsing.
11061106
HandLoadedModuleSet m_hand_loaded_modules;
1107-
1108-
private:
1109-
std::unique_ptr<SwiftPersistentExpressionState> m_persistent_state_up;
11101107
};
11111108

11121109
} // namespace lldb_private
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let comment = "Populate the persistent state with some fields"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
public func f() -> String { return "Hello from the Dylib!" }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Dylib
2+
f()
3+
let comment = "and back again"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
all: Dylib.framework
2+
3+
Dylib.framework: Dylib.swift
4+
"$(MAKE)" -f $(MAKEFILE_RULES) \
5+
FRAMEWORK=Dylib \
6+
DYLIB_SWIFT_SOURCES=Dylib.swift \
7+
DYLIB_NAME=Dylib \
8+
SWIFTFLAGS_EXTRAS="-Xcc -DNEW_OPTION_FROM_DYLIB=1" \
9+
MAKE_DSYM=YES
10+
11+
include ../Makefile.common
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// PlaygroundStub.swift
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
// -----------------------------------------------------------------------------
12+
13+
@_silgen_name ("playground_logger_initialize") func builtin_initialize();
14+
@_silgen_name ("GetOutput") func get_output() -> String;
15+
16+
builtin_initialize();
17+
18+
print(""); // Set breakpoint here
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// PlaygroundsRuntime.swift
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
// -----------------------------------------------------------------------------
12+
13+
// If you're modifying this file to add or modify function signatures, you
14+
// should be notifying the maintainer of PlaygroundLogger and also the
15+
// maintainer of lib/Sema/PlaygroundTransform.cpp.
16+
17+
var PlaygroundOutput = ""
18+
19+
struct SourceRange {
20+
let sl : Int
21+
let el : Int
22+
let sc : Int
23+
let ec : Int
24+
var text : String {
25+
return "[\(sl):\(sc)-\(el):\(ec)]"
26+
}
27+
}
28+
29+
struct ModuleFileIdentifier {
30+
let moduleId : Int
31+
let fileId : Int
32+
var text : String {
33+
return "[\(moduleId):\(fileId)]"
34+
}
35+
}
36+
37+
class LogRecord {
38+
let text : String
39+
40+
init(api : String, object : Any, name : String, id : Int, range : SourceRange, moduleFileId : ModuleFileIdentifier) {
41+
var object_description : String = ""
42+
print(object, terminator: "", to: &object_description)
43+
text = moduleFileId.text + " " + range.text + " " + api + "[" + name + "='" + object_description + "']"
44+
}
45+
init(api : String, object : Any, name : String, range : SourceRange, moduleFileId : ModuleFileIdentifier) {
46+
var object_description : String = ""
47+
print(object, terminator: "", to: &object_description)
48+
text = moduleFileId.text + " " + range.text + " " + api + "[" + name + "='" + object_description + "']"
49+
}
50+
init(api : String, object: Any, range : SourceRange, moduleFileId : ModuleFileIdentifier) {
51+
var object_description : String = ""
52+
print(object, terminator: "", to: &object_description)
53+
text = moduleFileId.text + " " + range.text + " " + api + "['" + object_description + "']"
54+
}
55+
init(api: String, range : SourceRange, moduleFileId : ModuleFileIdentifier) {
56+
text = moduleFileId.text + " " + range.text + " " + api
57+
}
58+
}
59+
60+
@_silgen_name ("playground_logger_initialize") public func builtin_initialize() {
61+
}
62+
63+
@_silgen_name ("playground_log_hidden") public func builtin_log_with_id<T>(_ object : T, _ name : String, _ id : Int, _ sl : Int, _ el : Int, _ sc : Int, _ ec: Int, _ moduleId : Int, _ fileId : Int) -> AnyObject? {
64+
let moduleFileId = ModuleFileIdentifier(moduleId:moduleId, fileId:fileId)
65+
return LogRecord(api:"__builtin_log", object:object, name:name, id:id, range : SourceRange(sl:sl, el:el, sc:sc, ec:ec), moduleFileId:moduleFileId)
66+
}
67+
68+
@_silgen_name ("playground_log_scope_entry") public func builtin_log_scope_entry(_ sl : Int, _ el : Int, _ sc : Int, _ ec: Int, _ moduleId : Int, _ fileId : Int) -> AnyObject? {
69+
let moduleFileId = ModuleFileIdentifier(moduleId:moduleId, fileId:fileId)
70+
return LogRecord(api:"__builtin_log_scope_entry", range : SourceRange(sl:sl, el:el, sc:sc, ec:ec), moduleFileId:moduleFileId)
71+
}
72+
73+
@_silgen_name ("playground_log_scope_exit") public func builtin_log_scope_exit(_ sl : Int, _ el : Int, _ sc : Int, _ ec: Int, _ moduleId : Int, _ fileId : Int) -> AnyObject? {
74+
let moduleFileId = ModuleFileIdentifier(moduleId:moduleId, fileId:fileId)
75+
return LogRecord(api:"__builtin_log_scope_exit", range : SourceRange(sl:sl, el:el, sc:sc, ec:ec), moduleFileId:moduleFileId)
76+
}
77+
78+
@_silgen_name ("playground_log_postprint") public func builtin_postPrint(_ sl : Int, _ el : Int, _ sc : Int, _ ec: Int, _ moduleId : Int, _ fileId : Int) -> AnyObject? {
79+
let moduleFileId = ModuleFileIdentifier(moduleId:moduleId, fileId:fileId)
80+
return LogRecord(api:"__builtin_postPrint", range : SourceRange(sl:sl, el:el, sc:sc, ec:ec), moduleFileId:moduleFileId)
81+
}
82+
83+
@_silgen_name ("DVTSendPlaygroundLogData") public func builtin_send_data(_ object:AnyObject?) {
84+
print((object as! LogRecord).text)
85+
PlaygroundOutput.append((object as! LogRecord).text)
86+
PlaygroundOutput.append("\n")
87+
}
88+
89+
@_silgen_name ("GetOutput") public func get_output() -> String {
90+
return PlaygroundOutput
91+
}
92+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import print_function
2+
import lldbsuite.test.lldbplaygroundrepl as repl
3+
from lldbsuite.test.lldbtest import *
4+
5+
class TestPlaygroundREPLImport(repl.PlaygroundREPLTest):
6+
7+
mydir = repl.PlaygroundREPLTest.compute_mydir(__file__)
8+
9+
def do_test(self):
10+
"""
11+
Test importing a library that adds new Clang options.
12+
"""
13+
self.expect('settings set target.swift-framework-search-paths "%s"' %
14+
self.getBuildDir())
15+
self.expect('settings set target.use-all-compiler-flags true')
16+
17+
log = self.getBuildArtifact('types.log')
18+
self.expect('log enable lldb types -f ' + log)
19+
result, playground_output = self.execute_code('BeforeImport.swift')
20+
self.assertIn("persistent", playground_output.GetSummary())
21+
result, playground_output = self.execute_code('Import.swift')
22+
self.assertIn("Hello from the Dylib", playground_output.GetSummary())
23+
self.assertIn("and back again", playground_output.GetSummary())
24+
25+
# Scan through the types log to make sure the SwiftASTContext was poisoned.
26+
self.filecheck('platform shell cat ""%s"' % log, __file__)
27+
# CHECK: New Swift image added{{.*}}Versions/A/Dylib{{.*}}ClangImporter needs to be reinitialized
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import Dylib
22
f()
3+
let comment = "and back again"

0 commit comments

Comments
 (0)