Firefox · Widget
CVE-2026-92048
Logic Error in Widget
Overview
Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
mExtentwidget/windows/WinOLELock.h |
modified | |
ifwidget/windows/nsDataObj.cpp |
modified | |
MakeScopeExitwidget/windows/nsDataObj.cpp |
modified | |
MakeScopeExitwidget/windows/nsDataObjCollection.cpp |
modified | |
ifwidget/windows/nsDataObjCollection.cpp |
modified |
Files Changed
widget/windows/WinOLELock.hwidget/windows/nsClipboard.cppwidget/windows/nsDataObj.cppwidget/windows/nsDataObjCollection.cppwidget/windows/nsDragService.cpp
Patch
diff --git a/widget/windows/WinOLELock.h b/widget/windows/WinOLELock.h
index 903a7891dba..6b6ae8c7001 100644
--- a/widget/windows/WinOLELock.h
+++ b/widget/windows/WinOLELock.h
@@ -11,6 +11,7 @@
#include <type_traits>
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
namespace details {
// implementation of `is_complete_type_v` borrowed from Raymond Chen:
@@ -171,9 +172,9 @@ class ScopedOLEMemory<U[]> {
static_assert(std::is_pod_v<U>, "type must be POD");
public:
- explicit ScopedOLEMemory(size_t n)
- : mHandle(::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(U) * n)),
- mExtent(n) {}
+ // Fail the allocation rather than under-allocating if the size computation
+ // would overflow; `lock()` hands out all `n` elements either way.
+ explicit ScopedOLEMemory(size_t n) : mHandle(AllocFor(n)), mExtent(n) {}
~ScopedOLEMemory() { ::GlobalFree(mHandle); }
ScopedOLELock<U[]> lock() const { return ScopedOLELock<U[]>(mHandle); }
@@ -187,6 +188,14 @@ class ScopedOLEMemory<U[]> {
}
private:
+ static HGLOBAL AllocFor(size_t n) {
+ mozilla::CheckedInt<size_t> const bytes =
+ mozilla::CheckedInt<size_t>(n) * sizeof(U);
+ return bytes.isValid()
+ ? ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes.value())
+ : nullptr;
+ }
+
HGLOBAL mHandle;
size_t mExtent;
};
diff --git a/widget/windows/nsClipboard.cpp b/widget/windows/nsClipboard.cpp
index 4ae1234bc2f..15359f6c902 100644
--- a/widget/windows/nsClipboard.cpp
+++ b/widget/windows/nsClipboard.cpp
@@ -914,7 +914,7 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject* aDataObject,
UINT const format = aFormat;
FORMATETC fe;
- STGMEDIUM stm;
+ STGMEDIUM stm{};
HRESULT hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
// If the format is CF_HDROP and we haven't found any files we can try looking
diff --git a/widget/windows/nsDataObj.cpp b/widget/windows/nsDataObj.cpp
index 1ff8ceba971..c68793d6062 100644
--- a/widget/windows/nsDataObj.cpp
+++ b/widget/windows/nsDataObj.cpp
@@ -14,6 +14,7 @@
#include "WinUtils.h"
#include "imgIEncoder.h"
#include "imgITools.h"
+#include "mozilla/CheckedInt.h"
#include "mozilla/Components.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
@@ -743,8 +744,7 @@ STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM) {
dfInx < mDataFlavors.Length()) {
nsCString const& df = mDataFlavors.ElementAt(dfInx);
if (FormatsMatch(fe, *aFormat)) {
- pSTM->pUnkForRelease =
- nullptr; // caller is responsible for deleting this data
+ *pSTM = STGMEDIUM{};
CLIPFORMAT const format = aFormat->cfFormat;
// compile-time-constant format indicators:
@@ -1648,22 +1648,44 @@ HRESULT nsDataObj::GetText(const nsACString& aDataFlavor, FORMATETC& aFE,
if (aFE.cfFormat == CF_TEXT) {
// Someone is asking for text/plain; convert the unicode (assuming it's
// present) to text with the correct platform encoding.
- size_t bufferSize = sizeof(char) * (len + 2);
- char* plainTextData = static_cast<char*>(moz_xmalloc(bufferSize));
+ //
+ // One UTF-16 code unit can encode to more than two bytes: U+0800..U+FFFF
+ // takes three when the ANSI code page is UTF-8. Size the buffer from the
+ // code page's own maximum rather than assuming two.
+ // The code page is fixed until a Windows reboot.
+ static UINT sMaxCharSize = []() {
+ CPINFO cpInfo;
+ if (!::GetCPInfo(CP_ACP, &cpInfo)) {
+ MOZ_ASSERT_UNREACHABLE("Couldn't get code page info?");
+ // The max ANSI code page character size at the moment is four bytes.
+ return 4u;
+ }
+ return cpInfo.MaxCharSize;
+ }();
+
+ // |len| is a byte count; the conversion counts UTF-16 code units, and
+ // includes the terminating null.
+ CheckedInt<int> const unitCount = CheckedInt<int>(len) / 2 + 1;
+ CheckedInt<int> const bufferSize = unitCount * sMaxCharSize;
+ if (!bufferSize.isValid()) {
+ return E_FAIL;
+ }
+
+ char* plainTextData = static_cast<char*>(moz_xmalloc(bufferSize.value()));
auto const _release =
mozilla::MakeScopeExit([plainTextData]() { ::free(plainTextData); });
char16_t* castedUnicode = reinterpret_cast<char16_t*>(data);
- int32_t plainTextLen =
- WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)castedUnicode, len / 2 + 1,
- plainTextData, bufferSize, NULL, NULL);
+ int32_t plainTextLen = WideCharToMultiByte(
+ CP_ACP, 0, (LPCWSTR)castedUnicode, unitCount.value(), plainTextData,
+ bufferSize.value(), NULL, NULL);
if (plainTextLen) {
return assignDataToStg(plainTextData, plainTextLen);
}
NS_WARNING("Oh no, couldn't convert unicode to plain text");
- return S_OK;
+ return E_FAIL;
}
if (aFE.cfFormat == nsClipboard::GetHtmlClipboardFormat()) {
@@ -1682,7 +1704,7 @@ HRESULT nsDataObj::GetText(const nsACString& aDataFlavor, FORMATETC& aFE,
}
NS_WARNING("Oh no, couldn't convert to HTML");
- return S_OK;
+ return E_FAIL;
}
// We assume that any data-format that isn't caught above can be satisfied by
diff --git a/widget/windows/nsDataObjCollection.cpp b/widget/windows/nsDataObjCollection.cpp
index e42d087ff61..36c7900af0d 100644
--- a/widget/windows/nsDataObjCollection.cpp
+++ b/widget/windows/nsDataObjCollection.cpp
@@ -62,6 +62,8 @@ STDMETHODIMP_(ULONG) nsDataObjCollection::Release() {
// IDataObject methods
STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
+ *pSTM = STGMEDIUM{};
+
static CLIPFORMAT fileDescriptorFlavorA =
::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
static CLIPFORMAT fileDescriptorFlavorW =
@@ -149,7 +151,6 @@ void nsDataObjCollection::AddDataObject(IDataObject* aDataObj) {
// Methods for getting data
HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
- STGMEDIUM workingmedium;
FORMATETC fe = *pFE;
HGLOBAL hGlobalMemory;
HRESULT hr;
@@ -163,6 +164,7 @@ HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
mozilla::MakeScopeExit([&]() { GlobalFree(hGlobalMemory); });
for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
+ STGMEDIUM workingmedium{};
nsDataObj* dataObj = mDataObjects.ElementAt(i);
hr = dataObj->GetData(&fe, &workingmedium);
if (hr != S_OK) {
@@ -173,6 +175,8 @@ HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
return hr;
}
}
+ auto releaseStgMedium =
+ mozilla::MakeScopeExit([&]() { ReleaseStgMedium(&workingmedium); });
// Now we need to pull out the filename
char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
if (buffer == nullptr) {
@@ -181,7 +185,6 @@ HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
buffer += sizeof(DROPFILES) / sizeof(char16_t);
filename = buffer;
GlobalUnlock(workingmedium.hGlobal);
- ReleaseStgMedium(&workingmedium);
// Now put the filename into our buffer
mozilla::CheckedInt<size_t> alloclen =
mozilla::CheckedInt<size_t>(filename.Length() + 1) * sizeof(char16_t);
@@ -231,7 +234,6 @@ HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
template <typename CharT, typename StringT>
HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
- STGMEDIUM workingmedium;
FORMATETC fe = *pFE;
HGLOBAL hGlobalMemory;
HRESULT hr;
@@ -243,6 +245,7 @@ HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
StringT text;
for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
+ STGMEDIUM workingmedium{};
nsDataObj* dataObj = mDataObjects.ElementAt(i);
hr = dataObj->GetData(&fe, &workingmedium);
if (hr != S_OK) {
@@ -253,6 +256,8 @@ HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
return hr;
}
}
+ auto releaseStgMedium =
+ mozilla::MakeScopeExit([&]() { ReleaseStgMedium(&workingmedium); });
// Now we need to pull out the text
CharT* buffer = static_cast<CharT*>(GlobalLock(workingmedium.hGlobal));
if (buffer == nullptr) {
@@ -260,7 +265,6 @@ HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
}
text = buffer;
GlobalUnlock(workingmedium.hGlobal);
- ReleaseStgMedium(&workingmedium);
// Now put the text into our buffer
mozilla::CheckedInt<size_t> alloclen =
mozilla::CheckedInt<size_t>(text.Length()) * sizeof(CharT);
@@ -296,7 +300,6 @@ HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
LPSTGMEDIUM pSTM,
bool aIsWideChar) {
- STGMEDIUM workingmedium;
FORMATETC fe = *pFE;
HGLOBAL hGlobalMemory;
HRESULT hr;
@@ -309,6 +312,7 @@ HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
mozilla::MakeScopeExit([&]() { GlobalFree(hGlobalMemory); });
for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
+ STGMEDIUM workingmedium{};
nsDataObj* dataObj = mDataObjects.ElementAt(i);
hr = dataObj->GetData(&fe, &workingmedium);
if (hr != S_OK) {
diff --git a/widget/windows/nsDragService.cpp b/widget/windows/nsDragService.cpp
index 2f016eaa785..a28995314c5 100644
--- a/widget/windows/nsDragService.cpp
+++ b/widget/windows/nsDragService.cpp
@@ -405,7 +405,7 @@ nsDragSession::GetNumDropItems(uint32_t* aNumItems) {
FORMATETC fe2;
SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
if (SUCCEEDED(mDataObject->QueryGetData(&fe2))) {
- STGMEDIUM stm;
+ STGMEDIUM stm{};
if (FAILED(mDataObject->GetData(&fe2, &stm))) {
*aNumItems = 1;
return NS_OK;
@@ -428,7 +428,7 @@ nsDragSession::GetNumDropItems(uint32_t* aNumItems) {
// Next check if we have a virtual file drop.
SET_FORMATETC(fe2, nsClipboard::GetClipboardFileDescriptorFormatW(), 0,
DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
- STGMEDIUM stm;
+ STGMEDIUM stm{};
if (SUCCEEDED(mDataObject->GetData(&fe2, &stm))) {
*aNumItems = 0;
Loading diff…
References
On This Page