HyperDbg/hyperdbg/script-engine/code/common.c

1638 lines
36 KiB
C
Raw Permalink Normal View History

2022-05-18 23:44:03 +04:30
/**
* @file common.c
* @author M.H. Gholamrezaei (mh@hyperdbg.org)
2023-01-18 20:23:40 +09:00
*
2022-05-18 23:44:03 +04:30
* @details Common routines
* @version 0.1
* @date 2020-10-22
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
2020-12-01 22:26:46 +03:30
/**
2022-05-18 16:00:16 +09:00
* @brief Allocates a new token
2020-12-01 22:26:46 +03:30
*
* @return PSCRIPT_ENGINE_TOKEN the allocated new unknown token
2020-12-01 22:26:46 +03:30
*/
PSCRIPT_ENGINE_TOKEN
2022-05-07 22:51:55 +09:00
NewUnknownToken()
2020-12-01 22:26:46 +03:30
{
PSCRIPT_ENGINE_TOKEN Token;
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
//
2022-05-07 22:51:55 +09:00
// Allocate memory for token and its value
2021-03-22 18:19:39 +04:30
//
Token = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
2024-03-17 20:33:02 +09:00
if (Token == NULL)
{
//
// There was an error allocating buffer
//
return NULL;
}
2022-03-21 18:17:17 +03:30
Token->Value = (char *)calloc(TOKEN_VALUE_MAX_LEN + 1, sizeof(char));
2020-12-01 22:26:46 +03:30
2024-03-17 20:33:02 +09:00
if (Token->Value == NULL)
{
//
// There was an error allocating buffer
//
free(Token);
2024-03-17 20:33:02 +09:00
return NULL;
}
2021-03-22 18:19:39 +04:30
//
// Init fields
//
2022-03-21 23:42:00 +03:30
strcpy(Token->Value, "");
2025-10-21 16:46:49 +08:00
Token->Type = UNKNOWN;
Token->Len = 0;
Token->MaxLen = TOKEN_VALUE_MAX_LEN;
2025-10-21 12:56:21 +02:00
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_LONG;
2025-10-21 16:46:49 +08:00
Token->VariableMemoryIdx = 0;
2026-07-18 22:28:45 +08:00
Token->AddressSpace = 0;
Token->IsAddress = FALSE;
Token->IsImplicitType = FALSE;
Token->IsSignedFunctionResult = FALSE;
2022-05-07 22:51:55 +09:00
return Token;
}
/**
* @brief Allocates a new token with given type and value
*
* @param Type the type of the token
* @param Value the value string of the token
* @return PSCRIPT_ENGINE_TOKEN the allocated new token
*/
PSCRIPT_ENGINE_TOKEN
NewToken(SCRIPT_ENGINE_TOKEN_TYPE Type, char * Value)
2022-05-07 22:51:55 +09:00
{
//
// Allocate memory for token]
//
PSCRIPT_ENGINE_TOKEN Token = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
2022-05-18 23:44:03 +04:30
2024-03-17 20:33:02 +09:00
if (Token == NULL)
{
//
// There was an error allocating buffer
//
return NULL;
}
2022-05-07 22:51:55 +09:00
//
// Init fields
//
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
// Note that MaxLen is never allowed to be zero here. AppendByte()/AppendWchar()
// test for a full buffer with 'Len >= MaxLen - 1' on an unsigned type, so a zero
// MaxLen would wrap around and let those routines write past the allocation
//
2025-10-21 16:46:49 +08:00
unsigned int Len = (unsigned int)strlen(Value);
Token->Type = Type;
Token->Len = Len;
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
Token->MaxLen = Len > TOKEN_VALUE_MAX_LEN ? Len : TOKEN_VALUE_MAX_LEN;
2025-10-21 16:46:49 +08:00
Token->Value = (char *)calloc(Token->MaxLen + 1, sizeof(char));
2025-10-21 12:56:21 +02:00
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_LONG;
2025-10-21 16:46:49 +08:00
Token->VariableMemoryIdx = 0;
2026-07-18 22:28:45 +08:00
Token->AddressSpace = 0;
Token->IsAddress = FALSE;
Token->IsImplicitType = FALSE;
Token->IsSignedFunctionResult = FALSE;
2024-03-17 20:33:02 +09:00
if (Token->Value == NULL)
{
//
// There was an error allocating buffer
//
free(Token);
2024-03-17 20:33:02 +09:00
return NULL;
}
2022-05-07 22:51:55 +09:00
strcpy(Token->Value, Value);
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
return Token;
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Removes allocated memory of a token
2020-12-01 22:26:46 +03:30
*
* @param Token
* @return void
2020-12-01 22:26:46 +03:30
*/
2021-03-22 18:19:39 +04:30
void
RemoveToken(PSCRIPT_ENGINE_TOKEN * Token)
2020-12-01 22:26:46 +03:30
{
2022-05-19 20:16:57 +09:00
free((*Token)->Value);
free(*Token);
*Token = NULL;
2021-03-22 18:19:39 +04:30
return;
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Prints token
* @details prints value and type of token
2020-12-01 22:26:46 +03:30
*
* @param Token the token to print
* @return void
2020-12-01 22:26:46 +03:30
*/
2021-03-22 18:19:39 +04:30
void
PrintToken(PSCRIPT_ENGINE_TOKEN Token)
2021-03-22 18:19:39 +04:30
{
//
2024-03-17 18:49:51 +09:00
// Prints value of the Token
2021-03-22 18:19:39 +04:30
//
if (Token->Type == WHITE_SPACE)
{
printf("< :");
}
else
{
printf("<'%s' : ", Token->Value);
}
//
// Prints type of the Token
//
switch (Token->Type)
{
case GLOBAL_ID:
printf(" GLOBAL_ID>\n");
2021-03-22 18:19:39 +04:30
break;
case GLOBAL_UNRESOLVED_ID:
printf(" GLOBAL_UNRESOLVED_ID>\n");
break;
case LOCAL_ID:
printf(" LOCAL_ID>\n");
break;
case LOCAL_UNRESOLVED_ID:
printf(" LOCAL_UNRESOLVED_ID>\n");
2021-06-27 19:48:25 +04:30
break;
case STATE_ID:
printf(" STATE_ID>\n");
break;
2021-03-22 18:19:39 +04:30
case DECIMAL:
printf(" DECIMAL>\n");
break;
case HEX:
printf(" HEX>\n");
break;
case OCTAL:
printf(" OCTAL>\n");
break;
case BINARY:
printf(" BINARY>\n");
break;
case SPECIAL_TOKEN:
printf(" SPECIAL_TOKEN>\n");
break;
case KEYWORD:
printf(" KEYWORD>\n");
break;
case WHITE_SPACE:
printf(" WHITE_SPACE>\n");
break;
case COMMENT:
printf(" COMMENT>\n");
break;
case REGISTER:
printf(" REGISTER>\n");
break;
case PSEUDO_REGISTER:
printf(" PSEUDO_REGISTER>\n");
break;
case SEMANTIC_RULE:
printf(" SEMANTIC_RULE>\n");
break;
case NON_TERMINAL:
printf(" NON_TERMINAL>\n");
break;
case END_OF_STACK:
printf(" END_OF_STACK>\n");
break;
case STRING:
printf(" STRING>\n");
break;
case WSTRING:
printf(" WSTRING>\n");
break;
2021-03-22 18:19:39 +04:30
case TEMP:
printf(" TEMP>\n");
break;
case UNKNOWN:
printf(" UNKNOWN>\n");
break;
2024-07-25 01:52:58 +08:00
case SCRIPT_VARIABLE_TYPE:
printf(" SCRIPT_VARIABLE_TYPE>\n");
break;
case FUNCTION_ID:
printf(" FUNCTION_ID>\n");
break;
case FUNCTION_PARAMETER_ID:
printf(" FUNCTION_PARAMETER_ID>\n");
break;
2025-10-21 16:46:49 +08:00
case DEFERENCE_TEMP:
printf(" DEFERENCE_TEMP>\n");
break;
2021-03-22 18:19:39 +04:30
default:
printf(" ERROR>\n");
break;
}
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Appends char to the token value
2020-12-01 22:26:46 +03:30
*
* @param Token
* @param c the character to append
* @return void
2020-12-01 22:26:46 +03:30
*/
2021-03-22 18:19:39 +04:30
void
AppendByte(PSCRIPT_ENGINE_TOKEN Token, char c)
2021-03-22 18:19:39 +04:30
{
//
// Check overflow of the string
//
2022-05-07 22:51:55 +09:00
if (Token->Len >= Token->MaxLen - 1)
2021-03-22 18:19:39 +04:30
{
//
// Double the length of the allocated space for the string
//
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
unsigned int NewMaxLen = Token->MaxLen * 2;
char * NewValue = (char *)calloc(NewMaxLen + 1, sizeof(char));
2021-03-22 18:19:39 +04:30
2024-03-17 20:33:02 +09:00
if (NewValue == NULL)
{
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
//
// MaxLen is only committed once the bigger buffer is in hand, otherwise
// it would describe a buffer that was never allocated and the next call
// would consider the (still small) buffer to have free space in it
//
2024-03-17 20:33:02 +09:00
printf("err, could not allocate buffer");
return;
}
2021-03-22 18:19:39 +04:30
//
// Free Old buffer and update the pointer
//
memcpy(NewValue, Token->Value, Token->Len);
2021-03-22 18:19:39 +04:30
free(Token->Value);
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
Token->Value = NewValue;
Token->MaxLen = NewMaxLen;
2021-03-22 18:19:39 +04:30
}
//
2024-03-17 18:49:51 +09:00
// Append the new character to the string
2021-03-22 18:19:39 +04:30
//
Token->Value[Token->Len] = c;
2022-05-07 22:51:55 +09:00
Token->Len++;
}
/**
* @brief Appends one fixed-width UTF-16 code unit to the token value
*
* @param Token
* @param c the wide character to append
* @return void
*/
void
AppendWchar(PSCRIPT_ENGINE_TOKEN Token, UINT16 c)
{
//
// Check overflow of the string
//
if (Token->Len >= Token->MaxLen - 2)
{
//
// Double the length of the allocated space for the wstring
//
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
unsigned int NewMaxLen = Token->MaxLen * 2;
char * NewValue = (char *)calloc(NewMaxLen + 2, sizeof(char));
2024-03-17 20:33:02 +09:00
if (NewValue == NULL)
{
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
//
// Keep MaxLen describing the buffer that is actually allocated, see the
// matching comment in AppendByte()
//
2024-03-17 20:33:02 +09:00
printf("err, could not allocate buffer");
return;
}
//
// Free Old buffer and update the pointer
//
memcpy(NewValue, Token->Value, Token->Len);
free(Token->Value);
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
Token->Value = NewValue;
Token->MaxLen = NewMaxLen;
}
//
2024-03-17 18:49:51 +09:00
// Append the new character to the wstring
//
memcpy(Token->Value + Token->Len, &c, sizeof(c));
Token->Len += 2;
}
2022-05-07 22:51:55 +09:00
/**
2022-05-18 16:00:16 +09:00
* @brief Copies a PTOKEN
2022-05-07 22:51:55 +09:00
*
* @param Token the token to copy
* @return PSCRIPT_ENGINE_TOKEN the copied token
2022-05-07 22:51:55 +09:00
*/
PSCRIPT_ENGINE_TOKEN
CopyToken(PSCRIPT_ENGINE_TOKEN Token)
2022-05-07 22:51:55 +09:00
{
PSCRIPT_ENGINE_TOKEN TokenCopy = (PSCRIPT_ENGINE_TOKEN)malloc(sizeof(SCRIPT_ENGINE_TOKEN));
2024-03-17 20:33:02 +09:00
if (TokenCopy == NULL)
{
//
// There was an error allocating buffer
//
return NULL;
}
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
//
// The number of bytes to copy is the larger of Len and the string length:
//
// - WSTRING tokens hold UTF-16 data whose embedded null bytes make strlen()
// (and strcpy()) stop early, so Len is the meaningful size for them
// - tokens taken from the static grammar tables in parse-table.c only have
// their Type and Value initialized, leaving Len at zero, so the string
// length is the meaningful size for those
//
unsigned int ValueLen = (unsigned int)strlen(Token->Value);
unsigned int CopyLen = Token->Len > ValueLen ? Token->Len : ValueLen;
unsigned int MaxLen = Token->MaxLen > CopyLen ? Token->MaxLen : CopyLen;
if (MaxLen < TOKEN_VALUE_MAX_LEN)
{
MaxLen = TOKEN_VALUE_MAX_LEN;
}
TokenCopy->Type = Token->Type;
//
// MaxLen describes the buffer that is actually allocated below. It used to be
// copied verbatim from the source token while the allocation was sized from the
// string length, so the two could disagree and let AppendByte()/AppendWchar()
// write past the end of the copy
//
TokenCopy->MaxLen = MaxLen;
TokenCopy->Len = Token->Len;
TokenCopy->Value = (char *)calloc(MaxLen + 2, sizeof(char));
TokenCopy->VariableType = Token->VariableType;
2026-07-18 22:28:45 +08:00
TokenCopy->VariableMemoryIdx = Token->VariableMemoryIdx;
TokenCopy->AddressSpace = Token->AddressSpace;
TokenCopy->IsAddress = Token->IsAddress;
TokenCopy->IsImplicitType = Token->IsImplicitType;
TokenCopy->IsSignedFunctionResult = Token->IsSignedFunctionResult;
2024-03-17 20:33:02 +09:00
if (TokenCopy->Value == NULL)
{
//
// There was an error allocating buffer
//
free(TokenCopy);
2024-03-17 20:33:02 +09:00
return NULL;
}
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
memcpy(TokenCopy->Value, Token->Value, CopyLen);
2022-05-18 23:44:03 +04:30
2022-05-07 22:51:55 +09:00
return TokenCopy;
2020-12-01 22:26:46 +03:30
}
/**
* @brief Allocates a new SCRIPT_ENGINE_TOKEN_LIST
2020-12-01 22:26:46 +03:30
*
* @return PSCRIPT_ENGINE_TOKEN_LIST the allocated token list
2020-12-01 22:26:46 +03:30
*/
PSCRIPT_ENGINE_TOKEN_LIST
2021-03-22 18:19:39 +04:30
NewTokenList(void)
2020-12-01 22:26:46 +03:30
{
PSCRIPT_ENGINE_TOKEN_LIST TokenList = NULL;
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
//
// Allocation of memory for SCRIPT_ENGINE_TOKEN_LIST structure
2021-03-22 18:19:39 +04:30
//
TokenList = (PSCRIPT_ENGINE_TOKEN_LIST)malloc(sizeof(*TokenList));
2020-12-01 22:26:46 +03:30
2024-03-17 20:33:02 +09:00
if (TokenList == NULL)
{
//
// There was an error allocating buffer
//
return NULL;
}
2021-03-22 18:19:39 +04:30
//
// Initialize fields of SCRIPT_ENGINE_TOKEN_LIST
2021-03-22 18:19:39 +04:30
//
TokenList->Pointer = 0;
TokenList->Size = TOKEN_LIST_INIT_SIZE;
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
//
// Allocation of memory for SCRIPT_ENGINE_TOKEN_LIST buffer
2021-03-22 18:19:39 +04:30
//
TokenList->Head = (PSCRIPT_ENGINE_TOKEN *)malloc(TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
2020-12-01 22:26:46 +03:30
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
if (TokenList->Head == NULL)
{
//
// There was an error allocating buffer
//
free(TokenList);
return NULL;
}
2021-03-22 18:19:39 +04:30
return TokenList;
2020-12-01 22:26:46 +03:30
}
/**
* @brief Removes allocated memory of a SCRIPT_ENGINE_TOKEN_LIST
2020-12-01 22:26:46 +03:30
*
* @param TokenList
* @return void
2020-12-01 22:26:46 +03:30
*/
2021-03-22 18:19:39 +04:30
void
RemoveTokenList(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
2020-12-01 22:26:46 +03:30
{
PSCRIPT_ENGINE_TOKEN Token;
2021-03-22 18:19:39 +04:30
for (uintptr_t i = 0; i < TokenList->Pointer; i++)
{
Token = *(TokenList->Head + i);
2022-05-19 20:16:57 +09:00
RemoveToken(&Token);
2021-03-22 18:19:39 +04:30
}
free(TokenList->Head);
free(TokenList);
2021-03-22 18:19:39 +04:30
return;
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Prints each Token inside a TokenList
2020-12-01 22:26:46 +03:30
*
* @param TokenList
* @return void
2020-12-01 22:26:46 +03:30
*/
2021-03-22 18:19:39 +04:30
void
PrintTokenList(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
2020-12-01 22:26:46 +03:30
{
PSCRIPT_ENGINE_TOKEN Token;
2021-03-22 18:19:39 +04:30
for (uintptr_t i = 0; i < TokenList->Pointer; i++)
{
Token = *(TokenList->Head + i);
PrintToken(Token);
}
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Adds Token to the last empty position of TokenList
2020-12-01 22:26:46 +03:30
*
* @param TokenList the token list to push into
* @param Token the token to add
* @return PSCRIPT_ENGINE_TOKEN_LIST
2020-12-01 22:26:46 +03:30
*/
PSCRIPT_ENGINE_TOKEN_LIST
Push(PSCRIPT_ENGINE_TOKEN_LIST TokenList, PSCRIPT_ENGINE_TOKEN Token)
2021-03-22 18:19:39 +04:30
{
//
// Calculate address to write new token
//
uintptr_t Head = (uintptr_t)TokenList->Head;
uintptr_t Pointer = (uintptr_t)TokenList->Pointer;
PSCRIPT_ENGINE_TOKEN * WriteAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
2021-03-22 18:19:39 +04:30
//
// Write Token to appropriate address in TokenList
//
*WriteAddr = Token;
//
// Update Pointer
//
TokenList->Pointer++;
//
// Handle overflow
//
if (Pointer == TokenList->Size - 1)
{
//
// Allocate a new buffer for string list with doubled length
//
PSCRIPT_ENGINE_TOKEN * NewHead = (PSCRIPT_ENGINE_TOKEN *)malloc(2 * TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
2021-03-22 18:19:39 +04:30
2024-03-17 20:33:02 +09:00
if (NewHead == NULL)
{
printf("err, could not allocate buffer");
return NULL;
}
2021-03-22 18:19:39 +04:30
//
// Copy old buffer to new buffer
//
memcpy(NewHead, TokenList->Head, TokenList->Size * sizeof(PSCRIPT_ENGINE_TOKEN));
2021-03-22 18:19:39 +04:30
//
// Free old buffer
//
free(TokenList->Head);
//
// Update Head and size of TokenList
//
TokenList->Size = TokenList->Size * 2;
TokenList->Head = NewHead;
}
return TokenList;
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Removes last Token of a TokenList and returns it
2020-12-01 22:26:46 +03:30
*
* @param TokenList
* @return PSCRIPT_ENGINE_TOKEN
2020-12-01 22:26:46 +03:30
*/
PSCRIPT_ENGINE_TOKEN
Pop(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
//
// Calculate address to read most recent token
//
if (TokenList->Pointer > 0)
TokenList->Pointer--; // not consider what if the token's type is string or wstring
uintptr_t Head = (uintptr_t)TokenList->Head;
uintptr_t Pointer = (uintptr_t)TokenList->Pointer;
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
return *ReadAddr;
2020-12-01 22:26:46 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Returns last Token of a TokenList
2020-12-01 22:26:46 +03:30
*
* @param TokenList
* @return PSCRIPT_ENGINE_TOKEN
2020-12-01 22:26:46 +03:30
*/
PSCRIPT_ENGINE_TOKEN
Top(PSCRIPT_ENGINE_TOKEN_LIST TokenList)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
//
// Calculate address to read most recent pushed token
2021-03-22 18:19:39 +04:30
//
uintptr_t Head = (uintptr_t)TokenList->Head;
uintptr_t Pointer = (uintptr_t)TokenList->Pointer - 1;
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
2020-12-01 22:26:46 +03:30
2021-03-22 18:19:39 +04:30
return *ReadAddr;
2020-12-01 22:26:46 +03:30
}
/**
* @brief Returns the token at a specific index from the top of the token list
*
* @param TokenList the token list to index into
* @param Index the zero-based index from the top
* @return PSCRIPT_ENGINE_TOKEN
*/
PSCRIPT_ENGINE_TOKEN
TopIndexed(PSCRIPT_ENGINE_TOKEN_LIST TokenList, int Index)
{
2025-10-21 16:46:49 +08:00
uintptr_t Head = (uintptr_t)TokenList->Head;
uintptr_t Pointer = (uintptr_t)TokenList->Pointer - 1 - Index;
PSCRIPT_ENGINE_TOKEN * ReadAddr = (PSCRIPT_ENGINE_TOKEN *)(Head + Pointer * sizeof(PSCRIPT_ENGINE_TOKEN));
return *ReadAddr;
}
2020-12-01 22:26:46 +03:30
/**
2023-01-18 20:23:40 +09:00
* @brief Checks whether input char belongs to hexadecimal digit-set or not
*
* @param c the character to check
* @return char
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsHex(char c)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
return 1;
else
return 0;
2020-12-01 22:26:46 +03:30
}
/**
2023-01-18 20:23:40 +09:00
* @brief Checks whether input char belongs to decimal digit-set or not
*
* @param c the character to check
* @return char
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsDecimal(char c)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
if (c >= '0' && c <= '9')
return 1;
else
return 0;
2020-12-01 22:26:46 +03:30
}
/**
2023-01-18 20:23:40 +09:00
* @brief Checks whether input char belongs to alphabet set or not
*
* @param c the character to check
* @return char
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsLetter(char c)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
return 1;
else
{
return 0;
}
2020-12-01 22:26:46 +03:30
}
/**
* @brief Checks whether input char is underscore (_) or not
*
* @param c the character to check
* @return char
*/
char
IsUnderscore(char c)
{
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
if (c == '_')
return 1;
else
{
return 0;
}
}
2020-12-01 22:26:46 +03:30
/**
2023-01-18 20:23:40 +09:00
* @brief Checks whether input char belongs to binary digit-set or not
*
* @param c the character to check
* @return char
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsBinary(char c)
2020-12-01 22:26:46 +03:30
{
2021-03-22 18:19:39 +04:30
if (c == '0' || c == '1')
return 1;
else
{
return 0;
}
2020-12-01 22:26:46 +03:30
}
/**
2023-01-18 20:23:40 +09:00
* @brief Checks whether input char belongs to octal digit-set or not
*
* @param c the character to check
* @return char
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsOctal(char c)
{
if (c >= '0' && c <= '7')
return 1;
else
return 0;
}
PSCRIPT_ENGINE_TOKEN
NewTemp(PSCRIPT_ENGINE_ERROR_TYPE Error)
2021-03-22 18:19:39 +04:30
{
static unsigned int TempID = 0;
int i;
2021-03-22 18:19:39 +04:30
for (i = 0; i < MAX_TEMP_COUNT; i++)
{
if (CurrentUserDefinedFunction->TempMap[i] == 0)
2021-03-22 18:19:39 +04:30
{
TempID = i;
CurrentUserDefinedFunction->TempMap[i] = 1;
2021-03-22 18:19:39 +04:30
break;
}
}
if (i == MAX_TEMP_COUNT)
{
*Error = SCRIPT_ENGINE_ERROR_TEMP_LIST_FULL;
2021-03-22 18:19:39 +04:30
}
PSCRIPT_ENGINE_TOKEN Temp = NewUnknownToken();
char TempValue[8];
2021-03-22 18:19:39 +04:30
sprintf(TempValue, "%d", TempID);
strcpy(Temp->Value, TempValue);
2024-07-24 21:25:55 +08:00
Temp->Type = TEMP;
if (CurrentUserDefinedFunction->MaxTempNumber < (i + 1))
2024-07-24 21:25:55 +08:00
{
CurrentUserDefinedFunction->MaxTempNumber = i + 1;
2024-07-24 21:25:55 +08:00
}
2021-03-22 18:19:39 +04:30
return Temp;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Frees the memory allocated by Temp
2023-01-18 20:23:40 +09:00
*
* @param Temp the token representing the temporary variable
2026-05-31 16:38:21 +02:00
* @return VOID
2022-05-10 15:08:24 +04:30
*/
2026-05-31 16:38:21 +02:00
VOID
FreeTemp(PSCRIPT_ENGINE_TOKEN Temp)
2021-03-22 18:19:39 +04:30
{
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
if (Temp->Type != TEMP && Temp->Type != DEFERENCE_TEMP)
{
return;
}
//
// The index is derived from the token's textual value, so it is range-checked
// before indexing the MAX_TEMP_COUNT-entry map
//
INT Id = (INT)DecimalToInt(Temp->Value);
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
if (Id >= 0 && Id < MAX_TEMP_COUNT)
2021-03-22 18:19:39 +04:30
{
CurrentUserDefinedFunction->TempMap[Id] = 0;
}
}
2022-05-18 16:00:16 +09:00
/**
* @brief Checks whether this Token type is OneOpFunc1
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-18 16:00:16 +09:00
*/
2021-03-22 18:19:39 +04:30
char
IsType1Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-03-22 18:19:39 +04:30
{
unsigned int n = ONEOPFUNC1_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-03-22 18:19:39 +04:30
{
if (!strcmp(Operator->Value, OneOpFunc1[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is OneOpFunc2
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsType2Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-03-22 18:19:39 +04:30
{
unsigned int n = ONEOPFUNC2_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-03-22 18:19:39 +04:30
{
if (!strcmp(Operator->Value, OneOpFunc2[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is OperatorsTwoOperandList
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsTwoOperandOperator(PSCRIPT_ENGINE_TOKEN Operator)
2021-03-22 18:19:39 +04:30
{
2021-04-13 22:53:02 +04:30
unsigned int n = OPERATORS_TWO_OPERAND_LIST_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-03-22 18:19:39 +04:30
{
2021-04-13 22:53:02 +04:30
if (!strcmp(Operator->Value, OperatorsTwoOperandList[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is OperatorsOneOperandList
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-04-13 22:53:02 +04:30
char
IsOneOperandOperator(PSCRIPT_ENGINE_TOKEN Operator)
2021-04-13 22:53:02 +04:30
{
unsigned int n = OPERATORS_ONE_OPERAND_LIST_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-04-13 22:53:02 +04:30
{
if (!strcmp(Operator->Value, OperatorsOneOperandList[i]))
2021-03-22 18:19:39 +04:30
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is VarArgFunc1
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsType4Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-03-22 18:19:39 +04:30
{
unsigned int n = VARARGFUNC1_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-03-22 18:19:39 +04:30
{
if (!strcmp(Operator->Value, VarArgFunc1[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is ZeroOpFunc1
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsType5Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-03-22 18:19:39 +04:30
{
unsigned int n = ZEROOPFUNC1_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-03-22 18:19:39 +04:30
{
if (!strcmp(Operator->Value, ZeroOpFunc1[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is TwoOpFunc1
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-05-19 15:17:14 +04:30
char
IsType6Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-05-19 15:17:14 +04:30
{
unsigned int n = TWOOPFUNC1_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-05-19 15:17:14 +04:30
{
if (!strcmp(Operator->Value, TwoOpFunc1[i]))
2021-05-19 15:17:14 +04:30
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is TwoOpFunc2
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-09-08 18:28:58 +09:00
char
IsType7Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-09-08 18:28:58 +09:00
{
unsigned int n = TWOOPFUNC2_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-09-08 18:28:58 +09:00
{
if (!strcmp(Operator->Value, TwoOpFunc2[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token type is ThreeOpFunc1
2023-01-18 20:23:40 +09:00
*
* @param Operator the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-09-08 18:28:58 +09:00
char
IsType8Func(PSCRIPT_ENGINE_TOKEN Operator)
2021-09-08 18:28:58 +09:00
{
unsigned int n = THREEOPFUNC1_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
2021-09-08 18:28:58 +09:00
{
if (!strcmp(Operator->Value, ThreeOpFunc1[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is OneOpFunc3
*
* @param Operator the token to check
* @return char
*/
char
IsType9Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = ONEOPFUNC3_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, OneOpFunc3[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is TwoOpFunc3
*
* @param Operator the token to check
* @return char
*/
char
IsType10Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = TWOOPFUNC3_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, TwoOpFunc3[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is ThreeOpFunc3
*
* @param Operator the token to check
* @return char
*/
char
IsType11Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = THREEOPFUNC3_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, ThreeOpFunc3[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is OneOpFunc4
*
* @param Operator the token to check
* @return char
*/
char
IsType12Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = ONEOPFUNC4_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, OneOpFunc4[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is TwoOpFunc4
*
* @param Operator the token to check
* @return char
*/
char
IsType13Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = TWOOPFUNC4_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, TwoOpFunc4[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is ThreeOpFunc2
*
* @param Operator the token to check
* @return char
*/
char
IsType14Func(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = THREEOPFUNC2_LENGTH;
2024-03-17 20:33:02 +09:00
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, ThreeOpFunc2[i]))
{
return 1;
}
}
return 0;
}
2024-06-13 14:42:43 +09:00
/**
* @brief Checks whether this Token type is ThreeOpFunc4
*
* @param Operator the token to check
2024-06-13 14:42:43 +09:00
* @return char
*/
char
IsType15Func(PSCRIPT_ENGINE_TOKEN Operator)
2024-06-13 14:42:43 +09:00
{
unsigned int n = THREEOPFUNC4_LENGTH;
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, ThreeOpFunc4[i]))
{
return 1;
}
}
return 0;
}
2025-06-03 10:00:08 +02:00
/**
* @brief Checks whether this Token type is ZeroOpFunc2
*
* @param Operator the token to check
2025-06-03 10:00:08 +02:00
* @return char
*/
char
IsType16Func(PSCRIPT_ENGINE_TOKEN Operator)
2025-06-03 10:00:08 +02:00
{
unsigned int n = ZEROOPFUNC2_LENGTH;
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, ZeroOpFunc2[i]))
{
return 1;
}
}
return 0;
}
/**
* @brief Checks whether this Token type is assignment operator
*
* @param Operator the token to check
* @return char
*/
char
IsAssignmentOperator(PSCRIPT_ENGINE_TOKEN Operator)
{
unsigned int n = ASSIGNMENT_OPERATOR_LIST_LENGTH;
for (unsigned int i = 0; i < n; i++)
{
if (!strcmp(Operator->Value, AssignmentOperatorList[i]))
{
return 1;
}
}
return 0;
}
2020-12-02 02:32:49 +03:30
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token is noneterminal
* NoneTerminal token starts with capital letter
2023-01-18 20:23:40 +09:00
*
* @param Token the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsNoneTerminal(PSCRIPT_ENGINE_TOKEN Token)
2020-12-02 02:32:49 +03:30
{
2021-03-22 18:19:39 +04:30
if (Token->Value[0] >= 'A' && Token->Value[0] <= 'Z')
return 1;
else
return 0;
2020-12-02 02:32:49 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Checks whether this Token is semantic rule
* SemanticRule token starts with '@'
2023-01-18 20:23:40 +09:00
*
* @param Token the token to check
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsSemanticRule(PSCRIPT_ENGINE_TOKEN Token)
2020-12-02 02:32:49 +03:30
{
2021-03-22 18:19:39 +04:30
if (Token->Value[0] == '@')
return 1;
else
return 0;
2020-12-02 02:32:49 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Gets the Non Terminal Id object
2023-01-18 20:23:40 +09:00
*
* @param Token the token to get the non-terminal ID of
* @return int the non-terminal ID or INVALID
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
int
GetNonTerminalId(PSCRIPT_ENGINE_TOKEN Token)
2020-12-02 02:32:49 +03:30
{
2021-03-22 18:19:39 +04:30
for (int i = 0; i < NONETERMINAL_COUNT; i++)
{
if (!strcmp(Token->Value, NoneTerminalMap[i]))
return i;
}
2021-06-27 19:48:25 +04:30
return INVALID;
2020-12-02 02:32:49 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Gets the Terminal Id object
2023-01-18 20:23:40 +09:00
*
* @param Token the token to get the terminal ID of
* @return int the terminal ID or INVALID
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
int
GetTerminalId(PSCRIPT_ENGINE_TOKEN Token)
2021-03-22 18:19:39 +04:30
{
for (int i = 0; i < TERMINAL_COUNT; i++)
{
if (Token->Type == HEX)
{
if (!strcmp("_hex", TerminalMap[i]))
return i;
}
2026-07-20 15:03:45 +08:00
else if (Token->Type == FLOAT_LITERAL)
{
if (!strcmp("_float", TerminalMap[i]))
return i;
}
else if (Token->Type == GLOBAL_ID || Token->Type == GLOBAL_UNRESOLVED_ID)
{
if (!strcmp("_global_id", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == LOCAL_ID || Token->Type == LOCAL_UNRESOLVED_ID)
2021-03-22 18:19:39 +04:30
{
if (!strcmp("_local_id", TerminalMap[i]))
2021-03-22 18:19:39 +04:30
{
return i;
}
}
else if (Token->Type == FUNCTION_ID)
{
if (!strcmp("_function_id", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == FUNCTION_PARAMETER_ID)
{
if (!strcmp("_function_parameter_id", TerminalMap[i]))
{
return i;
}
}
2021-03-22 18:19:39 +04:30
else if (Token->Type == REGISTER)
{
if (!strcmp("_register", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == PSEUDO_REGISTER)
{
if (!strcmp("_pseudo_register", TerminalMap[i]))
{
return i;
}
}
2024-07-25 01:52:58 +08:00
else if (Token->Type == SCRIPT_VARIABLE_TYPE)
{
if (!strcmp("_script_variable_type", TerminalMap[i]))
{
return i;
}
}
2021-03-22 18:19:39 +04:30
else if (Token->Type == DECIMAL)
{
if (!strcmp("_decimal", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == BINARY)
{
if (!strcmp("_binary", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == OCTAL)
{
if (!strcmp("_octal", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == STRING)
{
if (!strcmp("_string", TerminalMap[i]))
{
return i;
}
}
else if (Token->Type == WSTRING)
{
if (!strcmp("_wstring", TerminalMap[i]))
{
return i;
}
}
2021-03-22 18:19:39 +04:30
else // Keyword
{
if (!strcmp(Token->Value, TerminalMap[i]))
return i;
}
}
2021-06-27 19:48:25 +04:30
return INVALID;
2020-12-02 02:32:49 +03:30
}
/**
2022-05-18 16:00:16 +09:00
* @brief Gets the Non Terminal Id object
2023-01-18 20:23:40 +09:00
*
* @param Token the token to get the non-terminal ID of
* @return int the non-terminal ID or INVALID
2022-05-10 15:08:24 +04:30
*/
2021-04-11 18:27:23 +04:30
int
LalrGetNonTerminalId(PSCRIPT_ENGINE_TOKEN Token)
{
2021-04-11 18:27:23 +04:30
for (int i = 0; i < LALR_NONTERMINAL_COUNT; i++)
{
if (!strcmp(Token->Value, LalrNoneTerminalMap[i]))
return i;
}
2021-06-27 19:48:25 +04:30
return INVALID;
}
/**
2022-05-18 16:00:16 +09:00
* @brief Gets the Terminal Id object
2023-01-18 20:23:40 +09:00
*
* @param Token the token to get the terminal ID of
* @return int the terminal ID or INVALID
2022-05-10 15:08:24 +04:30
*/
2021-04-11 18:27:23 +04:30
int
LalrGetTerminalId(PSCRIPT_ENGINE_TOKEN Token)
2021-04-11 18:27:23 +04:30
{
for (int i = 0; i < LALR_TERMINAL_COUNT; i++)
{
if (Token->Type == HEX)
{
if (!strcmp("_hex", LalrTerminalMap[i]))
return i;
}
2026-07-20 15:03:45 +08:00
else if (Token->Type == FLOAT_LITERAL)
{
if (!strcmp("_float", LalrTerminalMap[i]))
return i;
2021-04-11 18:27:23 +04:30
}
else if (Token->Type == GLOBAL_ID || Token->Type == GLOBAL_UNRESOLVED_ID)
{
if (!strcmp("_global_id", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == LOCAL_ID || Token->Type == LOCAL_UNRESOLVED_ID)
2021-04-11 18:27:23 +04:30
{
if (!strcmp("_local_id", LalrTerminalMap[i]))
2021-04-11 18:27:23 +04:30
{
return i;
}
}
else if (Token->Type == FUNCTION_ID)
{
if (!strcmp("_function_id", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == FUNCTION_PARAMETER_ID)
{
if (!strcmp("_function_parameter_id", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == SCRIPT_VARIABLE_TYPE)
{
if (!strcmp("_script_variable_type", LalrTerminalMap[i]))
{
return i;
}
}
2021-04-11 18:27:23 +04:30
else if (Token->Type == REGISTER)
{
if (!strcmp("_register", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == PSEUDO_REGISTER)
{
if (!strcmp("_pseudo_register", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == DECIMAL)
{
if (!strcmp("_decimal", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == BINARY)
{
if (!strcmp("_binary", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == OCTAL)
{
if (!strcmp("_octal", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == STRING)
{
if (!strcmp("_string", LalrTerminalMap[i]))
{
return i;
}
}
else if (Token->Type == WSTRING)
{
if (!strcmp("_wstring", LalrTerminalMap[i]))
{
return i;
}
}
2021-04-11 18:27:23 +04:30
else // Keyword
{
if (!strcmp(Token->Value, LalrTerminalMap[i]))
return i;
}
}
2021-06-27 19:48:25 +04:30
return INVALID;
}
2020-12-02 02:32:49 +03:30
/**
2024-03-17 18:49:51 +09:00
* @brief Checks whether the value and type of Token1 and Token2 are the same
2023-01-18 20:23:40 +09:00
*
* @param Token1 the first token to compare
* @param Token2 the second token to compare
2023-01-18 20:23:40 +09:00
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-04-11 18:27:23 +04:30
char
IsEqual(const PSCRIPT_ENGINE_TOKEN Token1, const PSCRIPT_ENGINE_TOKEN Token2)
2021-04-11 18:27:23 +04:30
{
if (Token1->Type == Token2->Type)
{
if (Token1->Type == SPECIAL_TOKEN)
{
if (!strcmp(Token1->Value, Token2->Value))
{
return 1;
}
}
else
{
return 1;
}
}
if (Token1->Type == GLOBAL_ID && Token2->Type == GLOBAL_UNRESOLVED_ID)
{
return 1;
}
if (Token1->Type == GLOBAL_UNRESOLVED_ID && Token2->Type == GLOBAL_ID)
2021-06-27 19:48:25 +04:30
{
return 1;
}
if (Token1->Type == LOCAL_ID && Token2->Type == LOCAL_UNRESOLVED_ID)
2021-06-27 19:48:25 +04:30
{
return 1;
}
if (Token1->Type == LOCAL_UNRESOLVED_ID && Token2->Type == LOCAL_ID)
{
return 1;
}
2021-04-11 18:27:23 +04:30
return 0;
}
2022-05-10 15:08:24 +04:30
/**
* @brief Set the Type object
2023-01-18 20:23:40 +09:00
*
* @param Val
* @param Type
* @return void
2022-05-10 15:08:24 +04:30
*/
2021-04-11 18:27:23 +04:30
void
SetType(unsigned long long * Val, unsigned char Type)
{
*Val = (unsigned long long int)Type;
}
2022-05-10 15:08:24 +04:30
/**
2023-01-18 20:23:40 +09:00
* @brief Converts an decimal string to a integer
*
* @param str the decimal string to convert
2023-01-18 20:23:40 +09:00
* @return unsigned long long int
2022-05-10 15:08:24 +04:30
*/
unsigned long long
2021-04-11 18:27:23 +04:30
DecimalToInt(char * str)
{
unsigned long long Acc = 0;
SIZE_T Len;
Len = strlen(str);
for (int i = 0; i < Len; i++)
2021-04-11 18:27:23 +04:30
{
Acc *= 10;
Acc += (str[i] - '0');
2021-04-11 18:27:23 +04:30
}
return Acc;
2021-04-11 18:27:23 +04:30
}
2022-05-10 15:08:24 +04:30
/**
2023-01-18 20:23:40 +09:00
* @brief Converts an decimal string to a signed integer
*
* @param str the decimal string to convert
* @return unsigned long long
2022-05-10 15:08:24 +04:30
*/
unsigned long long
2021-04-11 18:27:23 +04:30
DecimalToSignedInt(char * str)
{
long long Acc = 0;
SIZE_T Len;
2021-04-11 18:27:23 +04:30
if (str[0] == '-')
{
Len = strlen(str);
for (int i = 1; i < Len; i++)
2021-04-11 18:27:23 +04:30
{
Acc *= 10;
Acc += (str[i] - '0');
2021-04-11 18:27:23 +04:30
}
return -Acc;
2021-04-11 18:27:23 +04:30
}
else
{
Len = strlen(str);
for (int i = 0; i < Len; i++)
2021-04-11 18:27:23 +04:30
{
Acc *= 10;
Acc += (str[i] - '0');
2021-04-11 18:27:23 +04:30
}
return Acc;
2021-04-11 18:27:23 +04:30
}
}
2022-05-10 15:08:24 +04:30
/**
2023-01-18 20:23:40 +09:00
* @brief Converts an hexadecimal string to integer
*
* @param str the hexadecimal string to convert
* @return unsigned long long
2022-05-10 15:08:24 +04:30
*/
unsigned long long
2021-04-11 18:27:23 +04:30
HexToInt(char * str)
{
CHAR Temp;
SIZE_T Len = strlen(str);
unsigned long long Acc = 0;
for (int i = 0; i < Len; i++)
2021-04-11 18:27:23 +04:30
{
Acc <<= 4;
2021-04-11 18:27:23 +04:30
if (str[i] >= '0' && str[i] <= '9')
{
Temp = str[i] - '0';
2021-04-11 18:27:23 +04:30
}
else if (str[i] >= 'a' && str[i] <= 'f')
{
Temp = str[i] - 'a' + 10;
2021-04-11 18:27:23 +04:30
}
else
{
Temp = str[i] - 'A' + 10;
2021-04-11 18:27:23 +04:30
}
Acc += Temp;
2021-04-11 18:27:23 +04:30
}
return Acc;
2021-04-11 18:27:23 +04:30
}
2022-05-10 15:08:24 +04:30
/**
2023-01-18 20:23:40 +09:00
* @brief Converts an octal string to integer
*
* @param str the octal string to convert
* @return unsigned long long
2022-05-10 15:08:24 +04:30
*/
unsigned long long
2021-04-11 18:27:23 +04:30
OctalToInt(char * str)
{
SIZE_T Len;
unsigned long long Acc = 0;
Len = strlen(str);
for (int i = 0; i < Len; i++)
2021-04-11 18:27:23 +04:30
{
Acc <<= 3;
Acc += (str[i] - '0');
2021-04-11 18:27:23 +04:30
}
return Acc;
2020-12-02 02:32:49 +03:30
}
2022-05-10 15:08:24 +04:30
/**
2023-01-18 20:23:40 +09:00
* @brief Converts a binary string to integer
*
* @param str the binary string to convert
* @return unsigned long long
2022-05-10 15:08:24 +04:30
*/
unsigned long long
2021-03-22 18:19:39 +04:30
BinaryToInt(char * str)
{
SIZE_T Len;
unsigned long long Acc = 0;
Len = strlen(str);
for (int i = 0; i < Len; i++)
2021-03-22 18:19:39 +04:30
{
Acc <<= 1;
Acc += (str[i] - '0');
2021-03-22 18:19:39 +04:30
}
return Acc;
2021-04-11 18:27:23 +04:30
}
/**
* @brief Rotate a character array to the left by one time
*
* @param str the string to rotate
* @return void
*/
void
RotateLeftStringOnce(char * str)
{
INT Length = (INT)strlen(str);
CHAR Temp = str[0];
Fix memory-safety and robustness issues in script engine and PCI ID parser Code audit of the script engine's scanner/token handling and of the PCI ID database parser. Each of the issues below was reproduced against the current code before the fix and re-checked afterwards. script-engine/scanner.c * An unterminated string literal ("abc or L"abc) hung the scanner in an endless loop: sgetc() returns EOF without consuming input, and neither string loop tested for it, so the token grew until allocation failed. Both loops now stop at EOF and report the token as UNKNOWN. script-engine/common.c * AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether the larger buffer was actually allocated. After a failed allocation MaxLen described memory that did not exist and the next append wrote past the end of the old buffer. MaxLen is now committed only on success. * CopyToken() allocated strlen(Value) + 1 bytes but carried over the source token's Len and MaxLen, so the copy's advertised capacity did not match its allocation, and WSTRING payloads were truncated at their first embedded null byte. The copy is now sized from Len/MaxLen and copied by length, with a fallback to the string length for the grammar tokens in parse-table.c, which only initialize Type and Value. * NewToken() set MaxLen to the value length, which is zero for an empty value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so a zero MaxLen wrapped and disabled buffer growth entirely. * IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it, so '@rax|1' was lexed as one malformed register name instead of a register, an operator and a number. The pseudo-register path already compared against '_' directly. * NewTokenList() did not check the allocation of its Head buffer. * NewTemp() kept the last handed-out id in a static, so an exhausted temp list produced a token aliasing a temporary still in use, and it derived MaxTempNumber from an out-of-range index. It also dereferenced the new token without a null check. * FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value parsed out of the token text. * RotateLeftStringOnce() wrote to str[-1] when handed an empty string. libhyperdbg/debugger/misc/pci-id.cpp * The database file was read into a malloc(Length) buffer that was never null-terminated, while ReadLine() walks it with strchr(). Looking up an absent vendor scans to the end and reads past the allocation. * The matched Vendor was allocated with malloc() and its Devices list head was only assigned once a device line was parsed, so a vendor with no device entries left it uninitialized and FreeVendor() walked a garbage pointer. * FreeVendor() released the device and subdevice lists but never the Vendor itself, leaking one per lookup for every enumerated PCI device. * The file handle leaked when the buffer allocation failed, ftell() and fread() results were unused, and several error paths leaked the Vendor or the not-yet-linked Device/SubDevice. * strncmp() compared sizeof(VendorId) bytes, which is the size of the pointer rather than the length of a vendor id. * ReadLine() passed an unclamped count to strncpy_s(), which triggers the invalid parameter handler for a line longer than the destination. * GetVendorById() ignored the GetModuleFileName() result and overwrote the tail of the path buffer without checking the room left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM
2026-08-01 14:22:52 +00:00
//
// An empty string has nothing to rotate, and writing the saved character back
// would land on str[-1]
//
if (Length == 0)
{
return;
}
for (int i = 0; i < (Length - 1); i++)
{
str[i] = str[i + 1];
}
str[Length - 1] = Temp;
2024-03-17 20:33:02 +09:00
}