CVE-2023-30187 (C++, published 2023-08-14T13:15Z)
Description: value: An out of bounds memory access vulnerability in ONLYOFFICE DocumentServer 4.0.3 through 7.3.2 allows remote attackers to run arbitrary code via crafted JavaScript file.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-787 \u2014 Out-of-bounds Write
The product writes data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/ONLYOFFICE/core
Files changed:
DesktopEditor/doctrenderer/docbuilder_p.cpp
DesktopEditor/doctrenderer/docbuilder_p.h
DesktopEditor/doctrenderer/embed/jsc/jsc_NativeControl.mm
DesktopEditor/doctrenderer/embed/v8/v8_NativeControl.cpp
DesktopEditor/doctrenderer/js_internal/v8/v8_base.cpp
DesktopEditor/doctrenderer/js_internal/v8/v8_base.h
Commit message: Fix bug 60433


Vulnerable code:
			if (retValue)
			retValue->Clear();
			JSSmart<CJSValue> retNativeVal = m_context->runScript(commandA, try_catch);
			return true;
			JSSmart<CJSContextScope> context_scope = m_context->CreateContextScope();
			if (true)
			return !bIsBreak;
					CheckFileDir();
					if (0 == nReturnCode)
					return 0;
-(JSValue*) Save_AllocNative : (JSValue*)len;
-(JSValue*) Save_ReAllocNative : (JSValue*)pos : (JSValue*)len;
FUNCTION_WRAPPER_JS_1(Save_AllocNative, Save_AllocNative)
FUNCTION_WRAPPER_JS_2(Save_ReAllocNative, Save_ReAllocNative)
    #define CURRENTWRAPPER CNativeControlEmbed

    FUNCTION_WRAPPER_V8(_GetFilePath, GetFilePath)
    FUNCTION_WRAPPER_V8_1(_SetFilePath, SetFilePath)
    FUNCTION_WRAPPER_V8(_GetFileId, GetFileId)
    FUNCTION_WRAPPER_V8_1(_SetFileId, SetFileId)
    FUNCTION_WRAPPER_V8_1(_GetFileArrayBuffer, GetFileBinary)
    FUNCTION_WRAPPER_V8_1(_GetFontArrayBuffer, GetFontBinary)
    FUNCTION_WRAPPER_V8(_GetFontsDirectory, GetFontsDirectory)
    FUNCTION_WRAPPER_V8_1(_GetFileString, GetFileString)
    FUNCTION_WRAPPER_V8(_GetEditorType, GetEditorType)
    FUNCTION_WRAPPER_V8(_CheckNextChange, CheckNextChange)
    FUNCTION_WRAPPER_V8(_GetChangesCount, GetCountChanges)
    FUNCTION_WRAPPER_V8_1(_GetChangesFile, GetChangesFile)
    FUNCTION_WRAPPER_V8_1(_Save_AllocNative, Save_AllocNative)
    FUNCTION_WRAPPER_V8_2(_Save_ReAllocNative, Save_ReAllocNative)
    FUNCTION_WRAPPER_V8_2(_Save_End, Save_End)
    FUNCTION_WRAPPER_V8_1(_AddImageInChanges, AddImageInChanges)
    FUNCTION_WRAPPER_V8_1(_ConsoleLog, ConsoleLog)
    FUNCTION_WRAPPER_V8_3(_SaveChanges, SaveChanges)
    FUNCTION_WRAPPER_V8_1(_zipOpenFile, zipOpenFile)
    FUNCTION_WRAPPER_V8_1(_zipOpenFileBase64, zipOpenFileBase64)
    FUNCTION_WRAPPER_V8_1(_zipGetFileAsString, zipGetFileAsString)
    FUNCTION_WRAPPER_V8_1(_zipGetFileAsBinary, zipGetFileAsBinary)
    FUNCTION_WRAPPER_V8(_zipCloseFile, zipCloseFile)
    FUNCTION_WRAPPER_V8_1(_GetImageUrl, GetImageUrl)
    FUNCTION_WRAPPER_V8_1(_GetImageOriginalSize, GetImageOriginalSize)
    FUNCTION_WRAPPER_V8(_GetImagesPath, GetImagesPath)

    v8::Handle<v8::ObjectTemplate> CreateNativeControlTemplate(v8::Isolate* isolate)
    {
        v8::EscapableHandleScope handle_scope(isolate);

        v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(V8IsolateOneArg);
        result->SetInternalFieldCount(1);

        NSV8Objects::Template_Set(result, "SetFilePath",        _SetFilePath);
        NSV8Objects::Template_Set(result, "GetFilePath",        _GetFilePath);
        NSV8Objects::Template_Set(result, "SetFileId",          _SetFileId);
        NSV8Objects::Template_Set(result, "GetFileId",          _GetFileId);
        NSV8Objects::Template_Set(result, "GetFileBinary",      _GetFileArrayBuffer);
        NSV8Objects::Template_Set(result, "GetFontBinary",      _GetFontArrayBuffer);
        NSV8Objects::Template_Set(result, "GetFontsDirectory",  _GetFontsDirectory);
        NSV8Objects::Template_Set(result, "GetFileString",      _GetFileString);
        NSV8Objects::Template_Set(result, "GetEditorType",      _GetEditorType);
        NSV8Objects::Template_Set(result, "CheckNextChange",    _CheckNextChange);
        NSV8Objects::Template_Set(result, "GetCountChanges",    _GetChangesCount);
        NSV8Objects::Template_Set(result, "GetChangesFile",     _GetChangesFile);
        NSV8Objects::Template_Set(result, "Save_AllocNative",   _Save_AllocNative);
        NSV8Objects::Template_Set(result, "Save_ReAllocNative", _Save_ReAllocNative);
        NSV8Objects::Template_Set(result, "Save_End",           _Save_End);
        NSV8Objects::Template_Set(result, "AddImageInChanges",  _AddImageInChanges);
        NSV8Objects::Template_Set(result, "ConsoleLog",         _ConsoleLog);
        NSV8Objects::Template_Set(result, "SaveChanges",        _SaveChanges);
        NSV8Objects::Template_Set(result, "ZipOpen",            _zipOpenFile);
        NSV8Objects::Template_Set(result, "ZipOpenBase64",      _zipOpenFileBase64);
        NSV8Objects::Template_Set(result, "ZipFileAsString",    _zipGetFileAsString);
        NSV8Objects::Template_Set(result, "ZipFileAsBinary",    _zipGetFileAsBinary);
        NSV8Objects::Template_Set(result, "ZipClose",           _zipCloseFile);
        NSV8Objects::Template_Set(result, "getImageUrl",        _GetImageUrl);
        NSV8Objects::Template_Set(result, "getImagesDirectory", _GetImagesPath);
        NSV8Objects::Template_Set(result, "GetImageOriginalSize", _GetImageOriginalSize);

        return handle_scope.Escape(result);
    }

    // Без SaveChanges
    v8::Handle<v8::ObjectTemplate> CreateNativeControlTemplateBuilder(v8::Isolate* isolate)
    {
        v8::EscapableHandleScope handle_scope(isolate);

        v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(V8IsolateOneArg);
        result->SetInternalFieldCount(1);

        NSV8Objects::Template_Set(result, "SetFilePath",        _SetFilePath);
        NSV8Objects::Template_Set(result, "GetFilePath",        _GetFilePath);
        NSV8Objects::Template_Set(result, "SetFileId",          _SetFileId);
        NSV8Objects::Template_Set(result, "GetFileId",          _GetFileId);
        NSV8Objects::Template_Set(result, "GetFileBinary",      _GetFileArrayBuffer);
        NSV8Objects::Template_Set(result, "GetFontBinary",      _GetFontArrayBuffer);
        NSV8Objects::Template_Set(result, "GetFontsDirectory",  _GetFontsDirectory);
        NSV8Objects::Template_Set(result, "GetFileString",      _GetFileString);
        NSV8Objects::Template_Set(result, "GetEditorType",      _GetEditorType);
        NSV8Objects::Template_Set(result, "CheckNextChange",    _CheckNextChange);
        NSV8Objects::Template_Set(result, "GetCountChanges",    _GetChangesCount);
        NSV8Objects::Template_Set(result, "GetChangesFile",     _GetChangesFile);
        NSV8Objects::Template_Set(result, "Save_AllocNative",   _Save_AllocNative);
        NSV8Objects::Template_Set(result, "Save_ReAllocNative", _Save_ReAllocNative);
        NSV8Objects::Template_Set(result, "Save_End",           _Save_End);
        NSV8Objects::Template_Set(result, "AddImageInChanges",  _AddImageInChanges);
        NSV8Objects::Template_Set(result, "ConsoleLog",         _ConsoleLog);
        NSV8Objects::Template_Set(result, "ZipOpen",            _zipOpenFile);
        NSV8Objects::Template_Set(result, "ZipOpenBase64",      _zipOpenFileBase64);
        NSV8Objects::Template_Set(result, "ZipFileAsString",    _zipGetFileAsString);
        NSV8Objects::Template_Set(result, "ZipFileAsBinary",    _zipGetFileAsBinary);
        NSV8Objects::Template_Set(result, "ZipClose",           _zipCloseFile);
        NSV8Objects::Template_Set(result, "getImageUrl",        _GetImageUrl);
        NSV8Objects::Template_Set(result, "getImagesDirectory", _GetImagesPath);
        NSV8Objects::Template_Set(result, "GetImageOriginalSize", _GetImageOriginalSize);

        return handle_scope.Escape(result);
    }

    void CreateNativeObject(const v8::FunctionCallbackInfo<v8::Value>& args)
    {
        v8::Isolate* isolate = args.GetIsolate();
        v8::HandleScope scope(isolate);

        v8::Handle<v8::ObjectTemplate> NativeObjectTemplate = CreateNativeControlTemplate(isolate);
        CNativeControlEmbed* pNativeObject = new CNativeControlEmbed();

        v8::Local<v8::Object> obj = NativeObjectTemplate->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
        obj->SetInternalField(0, v8::External::New(CV8Worker::GetCurrent(), pNativeObject));

        args.GetReturnValue().Set(obj);
    }

    // Без SaveChanges
    void CreateNativeObjectBuilder(const v8::FunctionCallbackInfo<v8::Value>& args)
    {
        v8::Isolate* isolate = args.GetIsolate();
        v8::HandleScope scope(isolate);

        v8::Handle<v8::ObjectTemplate> NativeObjectTemplate = CreateNativeControlTemplateBuilder(isolate);
        CNativeControlEmbed* pNativeObject = new CNativeControlEmbed();

        v8::Local<v8::Object> obj = NativeObjectTemplate->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
        obj->SetInternalField(0, v8::External::New(CV8Worker::GetCurrent(), pNativeObject));

        args.GetReturnValue().Set(obj);
    }
    v8::Isolate* current = CV8Worker::GetCurrent();
    context->m_internal->m_global->Set(current, name.c_str(), v8::FunctionTemplate::New(current, NSNativeControl::CreateNativeObject));
    v8::Isolate* current = CV8Worker::GetCurrent();
    context->m_internal->m_global->Set(current, name.c_str(), v8::FunctionTemplate::New(current, NSNativeControl::CreateNativeObjectBuilder));

Fixed code:
	if (retValue)
		retValue->Clear();
	JSSmart<CJSValue> retNativeVal = m_context->runScript(commandA, try_catch);
	return true;
	JSSmart<CJSContextScope> context_scope = m_context->CreateContextScope();
	if (true)
	return !bIsBreak;
			CheckFileDir();
			if (0 == nReturnCode)
				return 0;
//-(JSValue*) Save_AllocNative : (JSValue*)len;
//-(JSValue*) Save_ReAllocNative : (JSValue*)pos : (JSValue*)len;
//FUNCTION_WRAPPER_JS_1(Save_AllocNative, Save_AllocNative)
//FUNCTION_WRAPPER_JS_2(Save_ReAllocNative, Save_ReAllocNative)
#define CURRENTWRAPPER CNativeControlEmbed

	FUNCTION_WRAPPER_V8(_GetFilePath, GetFilePath)
	FUNCTION_WRAPPER_V8_1(_SetFilePath, SetFilePath)
	FUNCTION_WRAPPER_V8(_GetFileId, GetFileId)
	FUNCTION_WRAPPER_V8_1(_SetFileId, SetFileId)
	FUNCTION_WRAPPER_V8_1(_GetFileArrayBuffer, GetFileBinary)
	FUNCTION_WRAPPER_V8_1(_GetFontArrayBuffer, GetFontBinary)
	FUNCTION_WRAPPER_V8(_GetFontsDirectory, GetFontsDirectory)
	FUNCTION_WRAPPER_V8_1(_GetFileString, GetFileString)
	FUNCTION_WRAPPER_V8(_GetEditorType, GetEditorType)
	FUNCTION_WRAPPER_V8(_CheckNextChange, CheckNextChange)
	FUNCTION_WRAPPER_V8(_GetChangesCount, GetCountChanges)
	FUNCTION_WRAPPER_V8_1(_GetChangesFile, GetChangesFile)
	FUNCTION_WRAPPER_V8_1(_Save_AllocNative, Save_AllocNative)
	FUNCTION_WRAPPER_V8_2(_Save_ReAllocNative, Save_ReAllocNative)
	FUNCTION_WRAPPER_V8_2(_Save_End, Save_End)
	FUNCTION_WRAPPER_V8_1(_AddImageInChanges, AddImageInChanges)
	FUNCTION_WRAPPER_V8_1(_ConsoleLog, ConsoleLog)
	FUNCTION_WRAPPER_V8_3(_SaveChanges, SaveChanges)
	FUNCTION_WRAPPER_V8_1(_zipOpenFile, zipOpenFile)
	FUNCTION_WRAPPER_V8_1(_zipOpenFileBase64, zipOpenFileBase64)
	FUNCTION_WRAPPER_V8_1(_zipGetFileAsString, zipGetFileAsString)
	FUNCTION_WRAPPER_V8_1(_zipGetFileAsBinary, zipGetFileAsBinary)
	FUNCTION_WRAPPER_V8(_zipCloseFile, zipCloseFile)
	FUNCTION_WRAPPER_V8_1(_GetImageUrl, GetImageUrl)
	FUNCTION_WRAPPER_V8_1(_GetImageOriginalSize, GetImageOriginalSize)
	FUNCTION_WRAPPER_V8(_GetImagesPath, GetImagesPath)

	v8::Handle<v8::ObjectTemplate> CreateNativeControlTemplate(v8::Isolate* isolate)
	{
		v8::EscapableHandleScope handle_scope(isolate);

		v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(V8IsolateOneArg);
		result->SetInternalFieldCount(1);

		NSV8Objects::Template_Set(result, "SetFilePath",        _SetFilePath);
		NSV8Objects::Template_Set(result, "GetFilePath",        _GetFilePath);
		NSV8Objects::Template_Set(result, "SetFileId",          _SetFileId);
		NSV8Objects::Template_Set(result, "GetFileId",          _GetFileId);
		NSV8Objects::Template_Set(result, "GetFileBinary",      _GetFileArrayBuffer);
		NSV8Objects::Template_Set(result, "GetFontBinary",      _GetFontArrayBuffer);
		NSV8Objects::Template_Set(result, "GetFontsDirectory",  _GetFontsDirectory);
		NSV8Objects::Template_Set(result, "GetFileString",      _GetFileString);
		NSV8Objects::Template_Set(result, "GetEditorType",      _GetEditorType);
		NSV8Objects::Template_Set(result, "CheckNextChange",    _CheckNextChange);
		NSV8Objects::Template_Set(result, "GetCountChanges",    _GetChangesCount);
		NSV8Objects::Template_Set(result, "GetChangesFile",     _GetChangesFile);
		//NSV8Objects::Template_Set(result, "Save_AllocNative",   _Save_AllocNative);
		//NSV8Objects::Template_Set(result, "Save_ReAllocNative", _Save_ReAllocNative);
		NSV8Objects::Template_Set(result, "Save_End",           _Save_End);
		NSV8Objects::Template_Set(result, "AddImageInChanges",  _AddImageInChanges);
		NSV8Objects::Template_Set(result, "ConsoleLog",         _ConsoleLog);
		NSV8Objects::Template_Set(result, "SaveChanges",        _SaveChanges);
		NSV8Objects::Template_Set(result, "ZipOpen",            _zipOpenFile);
		NSV8Objects::Template_Set(result, "ZipOpenBase64",      _zipOpenFileBase64);
		NSV8Objects::Template_Set(result, "ZipFileAsString",    _zipGetFileAsString);
		NSV8Objects::Template_Set(result, "ZipFileAsBinary",    _zipGetFileAsBinary);
		NSV8Objects::Template_Set(result, "ZipClose",           _zipCloseFile);
		NSV8Objects::Template_Set(result, "getImageUrl",        _GetImageUrl);
		NSV8Objects::Template_Set(result, "getImagesDirectory", _GetImagesPath);
		NSV8Objects::Template_Set(result, "GetImageOriginalSize", _GetImageOriginalSize);

		return handle_scope.Escape(result);
	}

	// Без SaveChanges
	v8::Handle<v8::ObjectTemplate> CreateNativeControlTemplateBuilder(v8::Isolate* isolate)
	{
		v8::EscapableHandleScope handle_scope(isolate);

		v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(V8IsolateOneArg);
		result->SetInternalFieldCount(1);

		NSV8Objects::Template_Set(result, "SetFilePath",        _SetFilePath);
		NSV8Objects::Template_Set(result, "GetFilePath",        _GetFilePath);
		NSV8Objects::Template_Set(result, "SetFileId",          _SetFileId);
		NSV8Objects::Template_Set(result, "GetFileId",          _GetFileId);
		NSV8Objects::Template_Set(result, "GetFileBinary",      _GetFileArrayBuffer);
		NSV8Objects::Template_Set(result, "GetFontBinary",      _GetFontArrayBuffer);
		NSV8Objects::Template_Set(result, "GetFontsDirectory",  _GetFontsDirectory);
		NSV8Objects::Template_Set(result, "GetFileString",      _GetFileString);
		NSV8Objects::Template_Set(result, "GetEditorType",      _GetEditorType);
		NSV8Objects::Template_Set(result, "CheckNextChange",    _CheckNextChange);
		NSV8Objects::Template_Set(result, "GetCountChanges",    _GetChangesCount);
		NSV8Objects::Template_Set(result, "GetChangesFile",     _GetChangesFile);
		//NSV8Objects::Template_Set(result, "Save_AllocNative",   _Save_AllocNative);
		//NSV8Objects::Template_Set(result, "Save_ReAllocNative", _Save_ReAllocNative);
		NSV8Objects::Template_Set(result, "Save_End",           _Save_End);
		NSV8Objects::Template_Set(result, "AddImageInChanges",  _AddImageInChanges);
		NSV8Objects::Template_Set(result, "ConsoleLog",         _ConsoleLog);
		NSV8Objects::Template_Set(result, "ZipOpen",            _zipOpenFile);
		NSV8Objects::Template_Set(result, "ZipOpenBase64",      _zipOpenFileBase64);
		NSV8Objects::Template_Set(result, "ZipFileAsString",    _zipGetFileAsString);
		NSV8Objects::Template_Set(result, "ZipFileAsBinary",    _zipGetFileAsBinary);
		NSV8Objects::Template_Set(result, "ZipClose",           _zipCloseFile);
		NSV8Objects::Template_Set(result, "getImageUrl",        _GetImageUrl);
		NSV8Objects::Template_Set(result, "getImagesDirectory", _GetImagesPath);
		NSV8Objects::Template_Set(result, "GetImageOriginalSize", _GetImageOriginalSize);

		return handle_scope.Escape(result);
	}

	void CreateNativeObject(const v8::FunctionCallbackInfo<v8::Value>& args)
	{
		v8::Isolate* isolate = args.GetIsolate();
		v8::HandleScope scope(isolate);

		if (CIsolateAdditionalData::CheckSingletonType(isolate, CIsolateAdditionalData::iadtSingletonNative))
		{
			args.GetReturnValue().Set(v8::Undefined(isolate));
			return;
		}

		v8::Handle<v8::ObjectTemplate> NativeObjectTemplate = CreateNativeControlTemplate(isolate);
		CNativeControlEmbed* pNativeObject = new CNativeControlEmbed();

		v8::Local<v8::Object> obj = NativeObjectTemplate->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
		obj->SetInternalField(0, v8::External::New(CV8Worker::GetCurrent(), pNativeObject));

		args.GetReturnValue().Set(obj);
	}

	// Без SaveChanges
	void CreateNativeObjectBuilder(const v8::FunctionCallbackInfo<v8::Value>& args)
	{
		v8::Isolate* isolate = args.GetIsolate();
		v8::HandleScope scope(isolate);

		if (CIsolateAdditionalData::CheckSingletonType(isolate, CIsolateAdditionalData::iadtSingletonNative))
		{
			args.GetReturnValue().Set(v8::Undefined(isolate));
			return;
		}

		v8::Handle<v8::ObjectTemplate> NativeObjectTemplate = CreateNativeControlTemplateBuilder(isolate);
		CNativeControlEmbed* pNativeObject = new CNativeControlEmbed();

		v8::Local<v8::Object> obj = NativeObjectTemplate->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
		obj->SetInternalField(0, v8::External::New(CV8Worker::GetCurrent(), pNativeObject));

		args.GetReturnValue().Set(obj);
	}
	v8::Isolate* current = CV8Worker::GetCurrent();
	context->m_internal->m_global->Set(current, name.c_str(), v8::FunctionTemplate::New(current, NSNativeControl::CreateNativeObject));
	v8::Isolate* current = CV8Worker::GetCurrent();
	context->m_internal->m_global->Set(current, name.c_str(), v8::FunctionTemplate::New(current, NSNativeControl::CreateNativeObjectBuilder));
		unsigned int nEmbedDataCount = m_internal->m_isolate->GetNumberOfDataSlots();
		for (unsigned int i = 0; i < nEmbedDataCount; ++i)
		{
			CIsolateAdditionalData* pAdditionData = (CIsolateAdditionalData*)m_internal->m_isolate->GetData(i);
			delete pAdditionData;
		}

class CIsolateAdditionalData
{
public:
	enum IsolateAdditionlDataType {
		iadtSingletonNative = 0,
		iadtUndefined = 255
	};

	IsolateAdditionlDataType m_eType;
public:
	CIsolateAdditionalData(const IsolateAdditionlDataType& type = iadtUndefined) { m_eType = type; }
	virtual ~CIsolateAdditionalData() {}

	static bool CheckSingletonType(v8::Isolate* isolate, const IsolateAdditionlDataType& type, const bool& isAdd = true)
	{
		unsigned int nCount = isolate->GetNumberOfDataSlots();
		for (unsigned int i = 0; i < nCount; ++i)
		{
			CIsolateAdditionalData* pData = (CIsolateAdditionalData*)isolate->GetData(i);
			if (pData->m_eType == type)
				return true;
		}

		if (isAdd)
		{
			isolate->SetData(nCount, (void*)(new CIsolateAdditionalData(type)));
		}

		return false;
	}
};