Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c64b06a

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
1 parent a04e121 commit c64b06a

File tree

12 files changed

+208
-22
lines changed

12 files changed

+208
-22
lines changed

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

+49-18
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@ LLDBNameLookup::LLDBNameLookup(
138138
SwiftExpressionParser::SILVariableMap &variable_map, SymbolContext &sc,
139139
ExecutionContextScope &exe_scope)
140140
: SILDebuggerClient(source_file.getASTContext()),
141-
m_log(GetLog(LLDBLog::Expressions)), m_source_file(source_file),
142-
m_variable_map(variable_map), m_sc(sc) {
141+
m_source_file(source_file), m_variable_map(variable_map), m_sc(sc) {
143142
source_file.getParentModule()->setDebugClient(this);
144143

145144
if (!m_sc.target_sp)
@@ -177,11 +176,15 @@ void LLDBNameLookup::RegisterTypeAliases(
177176

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

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

197200
if (Name.str().starts_with("$")) {
198-
LLDB_LOG(m_log,
201+
LLDB_LOG(GetLog(LLDBLog::Expressions),
199202
"[LLDBExprNameLookup::shouldGlobalize] Returning true to "
200203
"globalizing {0}",
201204
Name.str());
@@ -223,7 +226,7 @@ class LLDBExprNameLookup : public LLDBNameLookup {
223226
static unsigned counter = 0;
224227
unsigned count = counter++;
225228

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

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

@@ -242,7 +247,7 @@ class LLDBExprNameLookup : public LLDBNameLookup {
242247
return false;
243248

244249
LLDB_LOG(
245-
m_log,
250+
GetLog(LLDBLog::Expressions),
246251
"[LLDBExprNameLookup::lookupAdditions ({0})] Searching for \"{1}\"",
247252
count, NameStr);
248253

@@ -290,9 +295,10 @@ class LLDBExprNameLookup : public LLDBNameLookup {
290295
persistent_results);
291296

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

369375
/// A name lookup class for REPL and Playground mode.
370376
class LLDBREPLNameLookup : public LLDBNameLookup {
377+
const SwiftASTContextForExpressions *m_this_context;
378+
371379
public:
372380
LLDBREPLNameLookup(swift::SourceFile &source_file,
373381
SwiftExpressionParser::SILVariableMap &variable_map,
374-
SymbolContext &sc, ExecutionContextScope &exe_scope)
375-
: LLDBNameLookup(source_file, variable_map, sc, exe_scope) {}
382+
SymbolContext &sc, ExecutionContextScope &exe_scope,
383+
const SwiftASTContextForExpressions *this_context)
384+
: LLDBNameLookup(source_file, variable_map, sc, exe_scope),
385+
m_this_context(this_context) {}
376386

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

@@ -398,7 +411,7 @@ class LLDBREPLNameLookup : public LLDBNameLookup {
398411
return false;
399412

400413
LLDB_LOG(
401-
m_log,
414+
GetLog(LLDBLog::Expressions),
402415
"[LLDBREPLNameLookup::lookupAdditions ({0})] Searching for \"{1}\"",
403416
count, NameStr);
404417

@@ -420,6 +433,12 @@ class LLDBREPLNameLookup : public LLDBNameLookup {
420433

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

13761395
LLDBNameLookup *external_lookup;
13771396
if (m_options.GetPlaygroundTransformEnabled() || m_options.GetREPLEnabled()) {
1378-
external_lookup =
1379-
new LLDBREPLNameLookup(*source_file, variable_map, m_sc, *m_exe_scope);
1397+
external_lookup = new LLDBREPLNameLookup(*source_file, variable_map, m_sc,
1398+
*m_exe_scope, &m_swift_ast_ctx);
13801399
} else {
1381-
external_lookup =
1382-
new LLDBExprNameLookup(*source_file, variable_map, m_sc, *m_exe_scope);
1400+
external_lookup = new LLDBExprNameLookup(*source_file, variable_map, m_sc,
1401+
*m_exe_scope, &m_swift_ast_ctx);
13831402
}
13841403

13851404
// FIXME: This call is here just so that the we keep the
@@ -1758,8 +1777,15 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
17581777

17591778
// Signal that we want to retry the expression exactly once with a
17601779
// fresh SwiftASTContext.
1761-
if (retry)
1780+
if (retry) {
1781+
if (auto *persistent_state =
1782+
llvm::cast_or_null<SwiftPersistentExpressionState>(
1783+
m_sc.target_sp->GetPersistentExpressionStateForLanguage(
1784+
lldb::eLanguageTypeSwift)))
1785+
persistent_state->Clear();
1786+
17621787
return ParseResult::retry_fresh_context;
1788+
}
17631789

17641790
// Unrecoverable error.
17651791
return ParseResult::unrecoverable_error;
@@ -2023,6 +2049,11 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
20232049
return ParseResult::unrecoverable_error;
20242050
}
20252051

2052+
if (m_swift_ast_ctx.GetASTContext()->hadError()) {
2053+
DiagnoseSwiftASTContextError();
2054+
return ParseResult::unrecoverable_error;
2055+
}
2056+
20262057
{
20272058
std::lock_guard<std::recursive_mutex> global_context_locker(
20282059
IRExecutionUnit::GetLLVMGlobalContextMutex());

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class SwiftExpressionParser : public ExpressionParser {
212212
/// The container for the IR, to be JIT-compiled or interpreted.
213213
lldb::IRExecutionUnitSP m_execution_unit_sp;
214214
/// The AST context to build the expression into.
215+
/// A shared pointer of this is held by SwiftUserexpression.
215216
SwiftASTContextForExpressions &m_swift_ast_ctx;
216217
/// Used to manage the memory of a potential on-off context.
217218
//lldb::TypeSystemSP m_typesystem_sp;
@@ -254,7 +255,6 @@ class LLDBNameLookup : public swift::SILDebuggerClient {
254255
llvm::SmallVectorImpl<swift::TypeAliasDecl *> &type_aliases);
255256

256257
protected:
257-
Log *m_log;
258258
swift::SourceFile &m_source_file;
259259
SwiftExpressionParser::SILVariableMap &m_variable_map;
260260
SymbolContext m_sc;

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

+3
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

-3
Original file line numberDiff line numberDiff line change
@@ -1089,9 +1089,6 @@ class SwiftASTContextForExpressions : public SwiftASTContext {
10891089
/// These are the names of modules that we have loaded by hand into
10901090
/// the Contexts we make for parsing.
10911091
HandLoadedModuleSet m_hand_loaded_modules;
1092-
1093-
private:
1094-
std::unique_ptr<SwiftPersistentExpressionState> m_persistent_state_up;
10951092
};
10961093

10971094
} // namespace lldb_private
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let comment = "Populate the persistent state with some fields"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
public func f() -> String { return "Hello from the Dylib!" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Dylib
2+
f()
3+
let comment = "and back again"
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
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
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+
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
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)