Skip to content

Commit bbe5cc3

Browse files
committed
[MERGE #5206 @sethbrenith] SubString crash
Merge pull request #5206 from sethbrenith:user/sethb/string-uaf I recently updated LiteralStringWithPropertyStringPtr to refer to the PropertyRecord's copy of the string buffer (just like PropertyString does), which can save some memory. However, if the script then takes a substring of that string, it would keep an invalid pointer as `originalFullStringReference`, which could allow the buffer to be freed. The fix contains three parts: 1. Return the correct owning pointer from `LiteralStringWithPropertyStringPtr::GetOriginalStringReference`. 2. Add an assertion that would catch this bug eagerly, rather than only after a subsequent GC. 3. Update `SingleCharString::GetOriginalStringReference` to pass the new assertion. It was not a bug because single-char strings are pinned to a script context and copied upon marshaling, but this change makes its behavior more consistent with the other string types. Fixes OS:17362430
2 parents 34967a4 + 5b6611d commit bbe5cc3

File tree

8 files changed

+31
-3
lines changed

8 files changed

+31
-3
lines changed

lib/Runtime/Base/PropertyRecord.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ namespace Js
3030
friend class BuiltInPropertyRecords;
3131
friend class DOMBuiltInPropertyRecords;
3232

33+
#if DBG
34+
DEFINE_VTABLE_CTOR_NOBASE(PropertyRecord); // used for type assertions
35+
#endif
36+
3337
private:
3438
Field(PropertyId) pid;
3539
//Made this mutable so that we can set it for Built-In js property records when we are adding it.

lib/Runtime/Library/ConcatString.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ namespace Js
200200
this->SetBuffer(propertyRecord->GetBuffer());
201201
}
202202

203+
void const * LiteralStringWithPropertyStringPtr::GetOriginalStringReference()
204+
{
205+
// If we have a property record, it's the string owner. Otherwise,
206+
// the string is guaranteed to itself be an allocation block (as
207+
// was asserted during the constructor).
208+
return this->propertyRecord != nullptr ? static_cast<const void*>(this->propertyRecord) : this->GetString();
209+
}
210+
203211
RecyclableObject* LiteralStringWithPropertyStringPtr::CloneToScriptContext(ScriptContext* requestContext)
204212
{
205213
if (this->propertyRecord == nullptr)

lib/Runtime/Library/ConcatString.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace Js
2222
void GetPropertyRecordImpl(_Out_ PropertyRecord const** propRecord, bool dontLookupFromDictionary = false);
2323
virtual void CachePropertyRecord(_In_ PropertyRecord const* propertyRecord) override;
2424
void CachePropertyRecordImpl(_In_ PropertyRecord const* propertyRecord);
25+
virtual void const * GetOriginalStringReference() override;
2526

2627
virtual RecyclableObject* CloneToScriptContext(ScriptContext* requestContext) override;
2728

lib/Runtime/Library/JavascriptString.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ namespace Js
7474

7575
static const char16* GetSzHelper(JavascriptString *str) { return str->GetSz(); }
7676
virtual const char16* GetSz(); // Get string, NULL terminated
77-
virtual void const * GetOriginalStringReference(); // Get the original full string (Same as GetString() unless it is a SubString);
77+
virtual void const * GetOriginalStringReference(); // Get the allocated object that owns the original full string buffer
7878

7979
#if ENABLE_TTD
8080
//Get the associated property id for this string if there is on (e.g. it is a propertystring otherwise return Js::PropertyIds::_none)

lib/Runtime/Library/PropertyString.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace Js
5454
return static_cast<PropertyString *>(aValue);
5555
}
5656

57-
void const * PropertyString::GetOriginalStringReference()
57+
const void * PropertyString::GetOriginalStringReference()
5858
{
5959
// Property record is the allocation containing the string buffer
6060
return this->propertyRecordUsageCache.GetPropertyRecord();

lib/Runtime/Library/SingleCharString.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,10 @@ namespace Js
3434
return Anew(arena, SingleCharString, ch,
3535
scriptContext->GetLibrary()->GetStringTypeStatic());
3636
}
37+
38+
void const * SingleCharString::GetOriginalStringReference()
39+
{
40+
// The owning allocation for the string buffer is the string itself
41+
return this;
42+
}
3743
}

lib/Runtime/Library/SingleCharString.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Js
1616
static SingleCharString* New(char16 ch, ScriptContext* scriptContext);
1717
static SingleCharString* New(char16 ch, ScriptContext* scriptContext, ArenaAllocator* arena);
1818

19+
virtual void const * GetOriginalStringReference() override;
20+
1921
protected:
2022
DEFINE_VTABLE_CTOR(SingleCharString, JavascriptString);
2123

lib/Runtime/Library/SubString.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ namespace Js
3737
const char16 * subString = string->GetString() + start;
3838
void const * originalFullStringReference = string->GetOriginalStringReference();
3939

40+
#if SYSINFO_IMAGE_BASE_AVAILABLE
41+
AssertMsg(AutoSystemInfo::IsJscriptModulePointer((void*)originalFullStringReference)
42+
|| recycler->IsValidObject((void*)originalFullStringReference)
43+
|| (VirtualTableInfo<PropertyRecord>::HasVirtualTable((void*)originalFullStringReference) && ((PropertyRecord*)originalFullStringReference)->IsBound()),
44+
"Owning pointer for SubString must be static or GC pointer, or property record bound by thread allocator");
45+
#endif
46+
4047
return RecyclerNew(recycler, SubString, originalFullStringReference, subString, length, scriptContext);
4148
}
4249

@@ -69,7 +76,7 @@ namespace Js
6976
return UnsafeGetBuffer();
7077
}
7178

72-
const void * SubString::GetOriginalStringReference()
79+
void const * SubString::GetOriginalStringReference()
7380
{
7481
if (originalFullStringReference != nullptr)
7582
{

0 commit comments

Comments
 (0)