Skip to content

Commit 335e913

Browse files
authored
Merge pull request #18439 from rintaro/sourcekit-editoffset-rdar34206143
[SourceKit] Defensive guard for invalid offset and length in edit request
2 parents 87755a4 + 399fece commit 335e913

File tree

7 files changed

+92
-4
lines changed

7 files changed

+92
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let a = 1

test/SourceKit/Misc/edit_range.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %sourcekitd-test \
2+
// RUN: -req=open %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
3+
// RUN: -req=edit -offset=10 -length=0 -replace="swift" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift
4+
5+
// RUN: %sourcekitd-test \
6+
// RUN: -req=open %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
7+
// RUN: -req=edit -offset=5 -length=5 -replace="swift" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift
8+
9+
// 'offset' out of range.
10+
// RUN: not %sourcekitd-test \
11+
// RUN: -req=open %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
12+
// RUN: -req=edit -offset=11 -length=0 -replace="swift" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift \
13+
// RUN: 2>&1 | %FileCheck %s
14+
15+
// 'offset' + 'length' out of range.
16+
// RUN: not %sourcekitd-test \
17+
// RUN: -req=open %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
18+
// RUN: -req=edit -offset=5 -length=6 -replace="swift" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift \
19+
// RUN: 2>&1 | %FileCheck %s
20+
21+
// Out of range after edits.
22+
// RUN: not %sourcekitd-test \
23+
// RUN: -req=open %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
24+
// RUN: -req=edit -offset=5 -length=5 -replace="" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift == \
25+
// RUN: -req=edit -offset=6 -length=0 -replace="swift" %S/Inputs/10bytes.swift -- %S/Inputs/10bytes.swift \
26+
// RUN: 2>&1 | %FileCheck %s
27+
28+
// CHECK: 'offset' + 'length' is out of range

tools/SourceKit/include/SourceKit/Support/ImmutableTextBuffer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ friend class EditableTextBuffer;
138138

139139
ImmutableTextBufferRef getBuffer() const;
140140

141+
size_t getSize() const;
142+
141143
bool isFromSameBuffer(ImmutableTextSnapshotRef Other) const {
142144
return Other->EditableBuf.get() == EditableBuf.get();
143145
}
@@ -169,6 +171,10 @@ class EditableTextBuffer : public ThreadSafeRefCountedBase<EditableTextBuffer> {
169171
return getSnapshot()->getBuffer();
170172
}
171173

174+
size_t getSize() const {
175+
return getSnapshot()->getSize();
176+
}
177+
172178
ImmutableTextSnapshotRef insert(unsigned ByteOffset, StringRef Text);
173179
ImmutableTextSnapshotRef erase(unsigned ByteOffset, unsigned Length);
174180
ImmutableTextSnapshotRef replace(unsigned ByteOffset, unsigned Length,
@@ -178,6 +184,7 @@ class EditableTextBuffer : public ThreadSafeRefCountedBase<EditableTextBuffer> {
178184
ImmutableTextSnapshotRef addAtomicUpdate(ImmutableTextUpdateRef NewUpd);
179185
ImmutableTextBufferRef getBufferForSnapshot(
180186
const ImmutableTextSnapshot &Snap);
187+
size_t getSizeForSnapshot(const ImmutableTextSnapshot &Snap) const;
181188
void refresh();
182189
friend class ImmutableTextSnapshot;
183190
};

tools/SourceKit/lib/Support/ImmutableTextBuffer.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ ImmutableTextBufferRef ImmutableTextSnapshot::getBuffer() const {
8383
return EditableBuf->getBufferForSnapshot(*this);
8484
}
8585

86+
size_t ImmutableTextSnapshot::getSize() const {
87+
return EditableBuf->getSizeForSnapshot(*this);
88+
}
89+
8690
bool ImmutableTextSnapshot::precedesOrSame(ImmutableTextSnapshotRef Other) {
8791
assert(Other);
8892

@@ -239,6 +243,36 @@ ImmutableTextBufferRef EditableTextBuffer::getBufferForSnapshot(
239243
return ImmBuf;
240244
}
241245

246+
size_t EditableTextBuffer::getSizeForSnapshot(
247+
const ImmutableTextSnapshot &Snap) const {
248+
if (auto Buf = dyn_cast<ImmutableTextBuffer>(Snap.DiffEnd))
249+
return Buf->getText().size();
250+
ImmutableTextUpdateRef Next = Snap.DiffEnd->Next;
251+
// FIXME: dyn_cast_null does not work with IntrusiveRefCntPtr.
252+
if (Next)
253+
if (auto Buf = dyn_cast<ImmutableTextBuffer>(Next))
254+
return Buf->getText().size();
255+
256+
ImmutableTextBufferRef StartBuf = Snap.BufferStart;
257+
258+
// Find the last ImmutableTextBuffer.
259+
ImmutableTextUpdateRef Upd = StartBuf;
260+
while (Upd != Snap.DiffEnd) {
261+
Upd = Upd->Next;
262+
if (auto Buf = dyn_cast<ImmutableTextBuffer>(Upd))
263+
StartBuf = Buf;
264+
}
265+
266+
size_t Length = StartBuf->getText().size();
267+
Upd = StartBuf;
268+
while (Upd != Snap.DiffEnd) {
269+
Upd = Upd->Next;
270+
auto Edit = cast<ReplaceImmutableTextUpdate>(Upd);
271+
Length = Length - Edit->getLength() + Edit->getText().size();
272+
}
273+
return Length;
274+
}
275+
242276
// This should always be called under the mutex lock.
243277
void EditableTextBuffer::refresh() {
244278
while (CurrUpd->Next) {

tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,10 +1709,16 @@ ImmutableTextSnapshotRef SwiftEditorDocument::initializeText(
17091709

17101710
ImmutableTextSnapshotRef SwiftEditorDocument::replaceText(
17111711
unsigned Offset, unsigned Length, llvm::MemoryBuffer *Buf,
1712-
bool ProvideSemanticInfo) {
1712+
bool ProvideSemanticInfo, std::string &error) {
17131713

17141714
llvm::sys::ScopedLock L(Impl.AccessMtx);
17151715

1716+
// Validate offset and length.
1717+
if ((Offset + Length) > Impl.EditableBuffer->getSize()) {
1718+
error = "'offset' + 'length' is out of range";
1719+
return nullptr;
1720+
}
1721+
17161722
Impl.Edited = true;
17171723
llvm::StringRef Str = Buf->getBuffer();
17181724

@@ -2234,9 +2240,14 @@ void SwiftLangSupport::editorReplaceText(StringRef Name,
22342240
StringRef PreEditTextRef(BufferStart + Offset, Length);
22352241
PreEditText = PreEditTextRef.str();
22362242
}
2243+
std::string error;
22372244
Snapshot = EditorDoc->replaceText(Offset, Length, Buf,
2238-
Consumer.needsSemanticInfo());
2239-
assert(Snapshot);
2245+
Consumer.needsSemanticInfo(), error);
2246+
if (!Snapshot) {
2247+
assert(error.size());
2248+
Consumer.handleRequestError(error.c_str());
2249+
return;
2250+
}
22402251

22412252
llvm::Optional<SyntaxParsingCache> SyntaxCache = llvm::None;
22422253
if (EditorDoc->getSyntaxTree().hasValue()) {

tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ class SwiftEditorDocument :
8484
ArrayRef<const char *> Args);
8585
ImmutableTextSnapshotRef replaceText(unsigned Offset, unsigned Length,
8686
llvm::MemoryBuffer *Buf,
87-
bool ProvideSemanticInfo);
87+
bool ProvideSemanticInfo,
88+
std::string &error);
8889

8990
void updateSemaInfo();
9091

unittests/SourceKit/Support/ImmutableTextBufferTest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,32 @@ using namespace llvm;
1818

1919
TEST(EditableTextBuffer, Updates) {
2020
const char *Text = "hello world";
21+
size_t Length = strlen(Text);
2122

2223
EditableTextBufferManager BufMgr;
2324
EditableTextBufferRef EdBuf = BufMgr.getOrCreateBuffer("/a/test", Text);
2425
ImmutableTextBufferRef Buf = EdBuf->getBuffer();
2526

2627
EXPECT_EQ(Buf->getText(), Text);
28+
EXPECT_EQ(EdBuf->getSize(), Length);
2729

2830
Buf = EdBuf->insert(6, "all ")->getBuffer();
2931
EXPECT_EQ(Buf->getText(), "hello all world");
32+
EXPECT_EQ(EdBuf->getSize(), strlen("hello all world"));
3033

3134
Buf = EdBuf->erase(9, 6)->getBuffer();
3235
EXPECT_EQ(Buf->getText(), "hello all");
36+
EXPECT_EQ(EdBuf->getSize(), strlen("hello all"));
3337

3438
Buf = EdBuf->replace(0, 5, "yo")->getBuffer();
3539
EXPECT_EQ(Buf->getText(), "yo all");
40+
EXPECT_EQ(EdBuf->getSize(), strlen("yo all"));
3641

3742
EdBuf = BufMgr.resetBuffer("/a/test", Text);
3843
EdBuf->insert(6, "all ");
3944
EdBuf->erase(9, 6);
4045
EdBuf->replace(0, 5, "yo");
46+
EXPECT_EQ(EdBuf->getSize(), strlen("yo all"));
4147
Buf = EdBuf->getSnapshot()->getBuffer();
4248
EXPECT_EQ(Buf->getText(), "yo all");
4349

0 commit comments

Comments
 (0)