I'm working on a Node addon that encrypts data using Windows DPAPI. I'm passing two Javascript Uint8Array to the C++ code using NAN.
This is what the typescript interface looks like:
export interface DpapiBindings{
protectData(dataToEncrypt: Uint8Array, optionalEntropy: Uint8Array, scope: string): Uint8Array
}
I'd like to then create a Node::Buffer in the C++ code:
void ProtectData( Nan::NAN_METHOD_ARGS_TYPE info)
{
v8::Isolate* isolate = info.GetIsolate();
//
auto buffer = node::Buffer::Data(info[0]);
auto len = node::Buffer::Length(info[0]);
DATA_BLOB entropyBlob;
entropyBlob.pbData = nullptr;
if (!info[1]->IsNull())
{
entropyBlob.pbData = reinterpret_cast<BYTE*>(node::Buffer::Data(info[1]));
entropyBlob.cbData = node::Buffer::Length(info[1]);
}
DATA_BLOB dataIn;
DATA_BLOB dataOut;
// initialize input data
dataIn.pbData = reinterpret_cast<BYTE*>(buffer);
dataIn.cbData = len;
success = CryptProtectData(
&dataIn,
nullptr,
entropyBlob.pbData ? &entropyBlob : nullptr,
nullptr,
nullptr,
flags,
&dataOut);
auto returnBuffer = Nan::CopyBuffer(reinterpret_cast<const char*>(dataOut.pbData), dataOut.cbData).ToLocalChecked();
LocalFree(dataOut.pbData);
info.GetReturnValue().Set(returnBuffer);
}
I'm new to C++, my question is: When working with node::Buffer::Data and node::Buffer::Length in C++ code, and calling into CryptProtectData, do I need to worry about buffer overflows? If so, how do I protect against it? Should I be appending a null char to buffer and len?
No, you don't need to worry about overflow. The dataIn and dataOut structures are pointers with a length.
BufferCopy is not what you want to use though. You want to use: Nan::MaybeLocal<v8::Object> Nan::NewBuffer(char* data, uint32_t size).
https://github.com/nodejs/nan/blob/master/doc/buffers.md#api_nan_new_buffer
and make sure you free the dataOut.pbData memory when you're done (i see you are with the LocalFree call.) the reason it can't overflow is that CryptProtectData allocates that buffer based on the size it needs.
Related
I have read a few questions and topics on this issue. For example:
Warning message regarding stack size
But I can't really work out how I am to isolate the issue in the method:
void CCreateReportDlg::ReadOptions()
{
CHeadingsDlg dlgHeadings(this);
COptionsDlg dlgOptions(this);
CBrothersDlg dlgBrothers(this, FALSE);
CAutoAssignSettingsDlg dlgAssign(this);
CString strSection = theApp.GetActiveScheduleSection(_T("Options"));
// headings
m_strReportTitle = dlgHeadings.GetReportTitle();
dlgHeadings.GetHeadings(m_aryStrHeading);
m_eReportMode = dlgOptions.GetReportMode();
// brother data
dlgBrothers.GetBrotherData(SOUND, m_aryStrSoundWT, m_aryStrSoundSM, TRUE);
dlgBrothers.GetBrotherData(PLATFORM, m_aryStrPlatformWT, m_aryStrPlatformSM, TRUE);
dlgBrothers.GetBrotherData(MIKE, m_aryStrMikeWT, m_aryStrMikeSM, TRUE);
dlgBrothers.GetBrotherData(ATTENDANT, m_aryStrAttendWT, m_aryStrAttendSM, TRUE);
dlgBrothers.GetBrotherData(SOUND, m_aryStrBroSoundAll, TRUE);
dlgBrothers.GetBrotherData(PLATFORM, m_aryStrBroPlatformAll, TRUE);
dlgBrothers.GetBrotherData(MIKE, m_aryStrBroMikeAll, TRUE);
dlgBrothers.GetBrotherData(ATTENDANT, m_aryStrBroAttendAll, TRUE);
CCustomAssignManagerDlg::GetCustomAssignData(m_aryPtrAssign, TRUE, ASSIGN_SORT_HEADING);
if(m_eReportMode != MODE_MEETING)
{
// We need to update the arrays to only include the brothers that are available
// on both days (so the two arrays become identical)
ModifyBrotherArrays();
}
dlgBrothers.GetBrotherData(m_cbBrother);
// We need an array of unique names
// (without first to entries in combo)
// Used during printing and exporting (multi highlight)
dlgBrothers.GetBrotherData(m_aryStrNames);
CBrothersDlg::SortArray(m_aryStrNames);
// options
m_bExcludePlatformColumn = dlgOptions.ExcludePlatformColumn();
m_bExcludePlatformMikeColumn = dlgOptions.ExcludePlatformMikeColumn();
m_bExcludeAttendColumn = dlgOptions.ExcludeAttendantColumn();
m_iNumMikeUsers = dlgOptions.GetNumMikeUsers();
m_iNumSoundUsers = dlgOptions.GetNumSoundUsers();
m_iNumAttendUsers = dlgOptions.GetNumAttendants();
dlgOptions.GetMeetingDays(m_iDayWT, m_iDaySM, m_iDayWeek);
m_iDayWT++;
m_iDaySM++;
m_iDayWeek++;
dlgOptions.GetCustomDateOptions(m_bCustomDate, m_strDateFormat);
m_bAvoidBack2Back = dlgAssign.AvoidBack2Back();
m_bCheckForConflicts = dlgAssign.CheckForConflicts();
m_bSelectNames = dlgAssign.SelectNames();
theApp.ReadDateMapData(m_mapSPtrBroDates);
m_bShowNotes = (BOOL)theApp.GetNumberSetting(strSection, _T("ShowNotes"), (int)TRUE);
m_bNoticeBoard = (BOOL)theApp.GetNumberSetting(strSection, _T("NoticeBoard"), (int)FALSE);
dlgOptions.ReadAssignStatesInfoEx(m_byAryAutoAssignStates);
}
I know I can adjust the compiler and increase the stacksize to supress the error.
But what is the basic approach to identifying the route cause in my method?
The first bunch of variables are CStringArray. Then it is followed by a CPtrArray of objects, of type CUSTOM_ASSIGN_S:
typedef struct tagCustomAssignment
{
int iIndex;
CString strDescription;
CString strHeading;
BOOL bExcluded;
CStringArray aryStrBrothersAll;
CStringArray aryStrBrothersWT;
CStringArray aryStrBrothersSM;
BOOL bIncludeWT;
BOOL bIncludeTMS;
BOOL bFixed;
int iFixedType;
} CUSTOM_ASSIGN_S;
Again, none of the members of the above structure are large arrays [xxx]. The only other notable method is ReadDateMapData that reads an archive and fills another map.
But as mentioned, I don't know how to go about isolating the issue. There are no large arrays being define. One or two TCHAR file[_MAXPATH]; but that is about it. The rest of classes like CStringArray or CPtrArray etc..
Thank you in advance.
Update
Based on the comments provided I was able to establish that even just the variable declarations at the top:
CHeadingsDlg dlgHeadings(this);
COptionsDlg dlgOptions(this);
CBrothersDlg dlgBrothers(this, FALSE);
CAutoAssignSettingsDlg dlgAssign(this);
CString strSection = theApp.GetActiveScheduleSection(_T("Options"));
Would cause it to have a stack size of 18072 to begin with.
My main variables in this class were actually dialog objects which understandably are quite large due to all the member variables etc..
The reason I was using these dialogues was to gain access to application settings by using their public methods.
By changing these dialogue declarations to new / delete and adjusting the code I no longer get the stack size warning:
void CCreateReportDlg::ReadOptions()
{
CHeadingsDlg *pDlgHeadings = new CHeadingsDlg(this);
if (pDlgHeadings != NULL)
{
m_strReportTitle = pDlgHeadings->GetReportTitle();
pDlgHeadings->GetHeadings(m_aryStrHeading);
delete pDlgHeadings;
}
COptionsDlg *pDlgOptions = new COptionsDlg(this);
if (pDlgOptions != NULL)
{
m_eReportMode = pDlgOptions->GetReportMode();
m_bExcludePlatformColumn = pDlgOptions->ExcludePlatformColumn();
m_bExcludePlatformMikeColumn = pDlgOptions->ExcludePlatformMikeColumn();
m_bExcludeAttendColumn = pDlgOptions->ExcludeAttendantColumn();
m_iNumMikeUsers = pDlgOptions->GetNumMikeUsers();
m_iNumSoundUsers = pDlgOptions->GetNumSoundUsers();
m_iNumAttendUsers = pDlgOptions->GetNumAttendants();
pDlgOptions->ReadAssignStatesInfoEx(m_byAryAutoAssignStates);
pDlgOptions->GetCustomDateOptions(m_bCustomDate, m_strDateFormat);
pDlgOptions->GetMeetingDays(m_iDayWT, m_iDaySM, m_iDayWeek);
m_iDayWT++;
m_iDaySM++;
m_iDayWeek++;
delete pDlgOptions;
}
CBrothersDlg *pDlgBrothers = new CBrothersDlg(this, FALSE);
if (pDlgBrothers != NULL)
{
pDlgBrothers->GetBrotherData(SOUND, m_aryStrSoundWT, m_aryStrSoundSM, TRUE);
pDlgBrothers->GetBrotherData(PLATFORM, m_aryStrPlatformWT, m_aryStrPlatformSM, TRUE);
pDlgBrothers->GetBrotherData(MIKE, m_aryStrMikeWT, m_aryStrMikeSM, TRUE);
pDlgBrothers->GetBrotherData(ATTENDANT, m_aryStrAttendWT, m_aryStrAttendSM, TRUE);
pDlgBrothers->GetBrotherData(SOUND, m_aryStrBroSoundAll, TRUE);
pDlgBrothers->GetBrotherData(PLATFORM, m_aryStrBroPlatformAll, TRUE);
pDlgBrothers->GetBrotherData(MIKE, m_aryStrBroMikeAll, TRUE);
pDlgBrothers->GetBrotherData(ATTENDANT, m_aryStrBroAttendAll, TRUE);
pDlgBrothers->GetBrotherData(m_cbBrother);
// We need an array of unique names (without first two entries in combo)
// Used during printing and exporting (multi highlight)
pDlgBrothers->GetBrotherData(m_aryStrNames);
CBrothersDlg::SortArray(m_aryStrNames);
delete pDlgBrothers;
}
CAutoAssignSettingsDlg *pDlgAssign = new CAutoAssignSettingsDlg(this);
if (pDlgAssign != NULL)
{
m_bAvoidBack2Back = pDlgAssign->AvoidBack2Back();
m_bCheckForConflicts = pDlgAssign->CheckForConflicts();
m_bSelectNames = pDlgAssign->SelectNames();
delete pDlgAssign;
}
CCustomAssignManagerDlg::GetCustomAssignData(m_aryPtrAssign, TRUE, ASSIGN_SORT_HEADING);
if(m_eReportMode != MODE_MEETING)
{
// We need to update the arrays to only include the brothers that are available
// on both days (so the two arrays become identical)
ModifyBrotherArrays();
}
theApp.ReadDateMapData(m_mapSPtrBroDates);
CString strSection = theApp.GetActiveScheduleSection(_T("Options"));
m_bShowNotes = (BOOL)theApp.GetNumberSetting(strSection, _T("ShowNotes"), (int)TRUE);
m_bNoticeBoard = (BOOL)theApp.GetNumberSetting(strSection, _T("NoticeBoard"), (int)FALSE);
}
I am porting the openvr sample to jogl, after we created the binding with jna.
Almost at the end (before rendering the controllers and the tracking base stations), I got stuck trying to translate a char pointer in C to a String in Java.
C++ code here:
//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
// into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
if( unRequiredBufferLen == 0 )
return "";
char *pchBuffer = new char[ unRequiredBufferLen ];
unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
std::string sResult = pchBuffer;
delete [] pchBuffer;
return sResult;
}
GetStringTrackedDeviceProperty here:
/** Returns a string property. If the device index is not valid or the property is not a string type this function will
* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
* null. Strings will generally fit in buffers of k_unTrackingStringSize characters. */
virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;
Where VR_OUT_STRING() is defined here as:
# define VR_CLANG_ATTR(ATTR)
#define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" )
I have already done something similar where I had to call a function that expect the pointer to an array of TrackedDevicePose_t structures:
private TrackedDevicePose_t.ByReference trackedDevicePosesReference = new TrackedDevicePose_t.ByReference();
public TrackedDevicePose_t[] trackedDevicePose
= (TrackedDevicePose_t[]) trackedDevicePosesReference.toArray(VR.k_unMaxTrackedDeviceCount);
I created first the reference and then from it the actual array.
But here I can't have a class extending the char array..
private String getTrackedDeviceString(IVRSystem hmd, int device, int prop, IntBuffer propError) {
int requiredBufferLen = hmd.GetStringTrackedDeviceProperty.apply(device, prop, Pointer.NULL, 0, propError);
if(requiredBufferLen == 0) {
return "";
}
CharArray.ByReference charArrayReference = new CharArray.ByReference();
char[] cs = charArrayReference.toArray(requiredBufferLen);
return null;
}
Where apply (here) is:
public interface GetStringTrackedDeviceProperty_callback extends Callback {
int apply(int unDeviceIndex, int prop, Pointer pchValue, int unBufferSize, IntBuffer pError);
};
CharArray class, crap attempt here
Any ideas?
I've done some porting of C and C++ code to Java, and while it's probably horribly hacky, the best I've come up with to solve cases where a pointer to an int primitive or a char*/String is needed for a function call, is to create a small wrapper class with a single property, pass that object into the function, change the property as needed, and retrieve the new value after the function call. So something like:
public class StringPointer {
public String value = "";
}
StringPointer pchBuffer = new StringPointer();
unRequiredBufferLen = pHmd.GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
String sResult = pchBuffer.value;
and inside GetStringTrackedDeviceProperty()
...
pchValue.value = "some string";
...
In this case, you can use a String, since that's what your code is doing with the char* after the function call, but if it actually really needs to be a char[], you can just create char[] pchBuffer = new char[unRequiredBufferLen]; and pass that into the function. It will be just like you were using a char* in C++, and any changes you make inside the array will be visible after the function ends, and you can even do String sResult = new String(pchBuffer);.
I'm working with libuv (https://github.com/joyent/libuv) and nodejs v12. I I write native module, in this module i have struct:
struct Work_req
{
const char *data;
size_t data_length;
Isolate* isolate;
unsigned int callback_id;
Persistent<Function> callback;
};
Then i try to pass this structure to work:
Work_req* request = new Work_req;
request->data = buf->base;
request->data_length = (size_t)nread;
request->isolate = env->isolate();
request->callback_id = callback_id->ToNumber()->Value();
uv_work_t *req = new uv_work_t;
req->data = request;
uv_queue_work(env->event_loop(), req, findCallback, after_findCallback);
In the end of this code i passed work to loop and all is ok.
But when i'm trying to read data from uv_work_t *req in findCallback function, i get strange symbols there:
Work_req *s = ((struct Work_req*)req->data);
printf(s>data); //print char array from structure here
I see something like this:
����g�Kack_id":1,"error":null,"response":{"type":"test"}}�����
How i can fix it?
Thanks a lot.
We have some data in a text file which is built into our executable as a custom resource to be read at runtime. The size of this text file is over 7 million characters.
I can successfully search for and locate strings within the resource which appear near the top of the text file, but when attempting to search for terms a few million characters down, strstr returns NULL indicating that the string cannot be found. Is there a limit to the length of a string literal that can be stored in a char* or the amount of data that can be stored in an embedded resource? Code is shown below
char* data = NULL;
HINSTANCE hInst = NULL;
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_TEXT_FILE1), "TESTRESOURCE");
if(NULL != hRes)
{
HGLOBAL hData = LoadResource(hInst, hRes);
if (hData)
{
DWORD dataSize = SizeofResource(hInst, hRes);
data = (char*)LockResource(hData);
}
else
break;
char* pkcSearchResult = strstr(data, "NumListDetails");
if ( pkcSearchResult != NULL )
{
// parse data
}
}
Thanks.
The problem might be the method you use for searching. strstr uses ANSI strings, and will terminate when it encounters a '\0' in the search domain.
You might use something like memstr (one of many implementations can be found here).
Do you get any output from GetLastError(), specifically after calling SizeofResource.
You can also check that dataSize > 0 to ensure an error hasn't occurred.
DWORD dataSize = SizeofResource(hInst, hRes);
if(dataSize > 0)
{
data = (char*)LockResource(hData);
}
else
{
//check error codes
}
MSDN Docs
The problem was null characters in the data which prematurely ended the char* variable. To get around this I just had to read the data into a void pointer then copy it into a dynamically created array.
DWORD dataSize = SizeofResource(hInst, hRes);
void* pvData = LockResource(hData);
char* pcData = new char[dataSize];
memcpy_s(pcData,strlen(pcData),pvData,dataSize);
I have a working Visual Studio project.
I've created a static library with the files of the original project (except main.cpp), also
I've created a "tester" project (with the static lib linked to it) with only a main.cpp file from the original project.
Both compiles without any relevant error.
And tester runs appropriately.
But! At testing the "tester" I am getting a heap allocation error at a (not the first)
new[] operator invoked in a constructor implemented in the library.
That line working fine in the original project without any error.
The "little" version of the code:
//the staticlib
void test() {
manager* m = new manager;
m->open();
}
//....
class manager {
public:
open() {
PRAWINPUTDEVICELIST lDevList;
UINT lDevCount;
GetRawInputDeviceList(NULL, &lDevCount, sizeof(RAWINPUTDEVICELIST));
lDevList = (PRAWINPUTDEVICELIST) malloc(sizeof(RAWINPUTDEVICELIST)*lDevCount);
GetRawInputDeviceList(lDevList, &lDevCount, sizeof(RAWINPUTDEVICELIST));
if(lDevCount) {
for(UINT i = 0; i < lDevCount; i++) {
HIDP_CAPS mCaps;
PHIDP_BUTTON_CAPS mButtonCaps;
PHIDP_VALUE_CAPS mValueCaps;
UINT size;
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, NULL, &size);
char* name = new char[size+1];
//just to be sure
memset(name, 0, size+1);
//surely sure
name[size] = '\0';
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
HANDLE lDev = CreateFile((LPCWSTR)name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);;
PHIDP_PREPARSED_DATA lPrep;
HidD_GetPreparsedData(lDev, &lPrep);
HidP_GetCaps(lPrep, &mCaps);
if(mCaps.NumberInputButtonCaps) {
//crash is here below
//mCaps.NumberInputButtonCaps ~1
mButtonCaps = new HIDP_BUTTON_CAPS[mCaps.NumberInputButtonCaps];
HidP_GetButtonCaps(HidP_Input, mButtonCaps, &mCaps.NumberInputButtonCaps, lPrep);
}
if(mCaps.NumberInputValueCaps) {
//if the first "crash-line" is commented, then
//the crash is here
mValueCaps = new HIDP_VALUE_CAPS[mCaps.NumberInputValueCaps];
HidP_GetValueCaps(HidP_Input, mValueCaps, &mCaps.NumberInputValueCaps, lPrep);
}
CloseHandle(lDev);
}
}
}
};
//the app
test();
Where I am wrong? Is it a typical novice commission I am not afraid of?
Sorry for my English, and thanks ahead for your time!
The error is that you should be allocating wide chars when you call GetRawInputDeviceInfo. From the manual
RIDI_DEVICENAME 0x20000007
pData points to a string that contains the device name.
For this uiCommand only, the value in pcbSize is the character count
(not the byte count).
In other words you should write
wchar_t* name = new wchar_t[size];
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
Just from reading the manual, I have no actual experience with this API, but it seems a likely explanation.
Add logic that checks for error return states on every Win32 call you make. Possibly one of them is failing and when you remedy that, the rest will work. Always check for and handle errors when you are using Win32 APIs.