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

1323 lines
36 KiB
C
Raw Permalink Normal View History

2020-10-22 18:56:58 +03:30
/**
* @file scanner.c
2022-01-18 22:38:56 +03:30
* @author M.H. Gholamrezaei (mh@hyperdbg.org)
2023-01-18 20:23:40 +09:00
*
2022-05-18 23:44:03 +04:30
* @details Script Engine Scanner
2020-10-22 18:56:58 +03:30
* @version 0.1
* @date 2020-10-22
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
2020-10-20 19:35:15 +03:30
2026-07-18 22:28:45 +08:00
static BOOLEAN PreviousTokenCanEndExpression;
static PSCRIPT_ENGINE_TOKEN
ScanCharacterLiteral(PSCRIPT_ENGINE_TOKEN Token, char * c, char * str, BOOLEAN IsWide)
{
UINT32 Value = 0;
UINT32 Digits = 0;
UINT32 MaxHexDigits = IsWide ? 4 : 2;
*c = sgetc(str);
if ((int)*c == EOF || *c == '\'')
goto InvalidLiteral;
if (*c == '\\')
{
*c = sgetc(str);
switch (*c)
{
case 'n': Value = '\n'; *c = sgetc(str); break;
case 't': Value = '\t'; *c = sgetc(str); break;
case 'r': Value = '\r'; *c = sgetc(str); break;
case '0': Value = 0; *c = sgetc(str); break;
case '\\': Value = '\\'; *c = sgetc(str); break;
case '\'': Value = '\''; *c = sgetc(str); break;
case '"': Value = '"'; *c = sgetc(str); break;
case 'x':
*c = sgetc(str);
while (IsHex(*c) && Digits < MaxHexDigits)
{
Value = (Value << 4) | (UINT32)(*c <= '9' ? *c - '0' : ((*c | 0x20) - 'a' + 10));
Digits++;
*c = sgetc(str);
}
if (!Digits || IsHex(*c))
goto InvalidLiteral;
break;
default:
goto InvalidLiteral;
}
}
else
{
Value = (UINT8)*c;
*c = sgetc(str);
}
if (*c != '\'')
goto InvalidLiteral;
PlatformSnprintf(Token->Value, Token->MaxLen, "%x", Value);
Token->Len = (unsigned int)strlen(Token->Value);
Token->Type = HEX;
Token->VariableType = IsWide ? (VARIABLE_TYPE *)VARIABLE_TYPE_WCHAR : (VARIABLE_TYPE *)VARIABLE_TYPE_INT;
*c = sgetc(str);
return Token;
InvalidLiteral:
Token->Type = UNKNOWN;
return Token;
}
2020-10-20 19:35:15 +03:30
/**
2022-05-10 15:08:24 +04:30
* @brief reads a token from the input string
2023-01-18 20:23:40 +09:00
*
* @param c
* @param str
* @return PSCRIPT_ENGINE_TOKEN
2022-05-10 15:08:24 +04:30
*/
PSCRIPT_ENGINE_TOKEN
2021-03-22 18:19:39 +04:30
GetToken(char * c, char * str)
2020-10-20 19:35:15 +03:30
{
PSCRIPT_ENGINE_TOKEN Token = NewUnknownToken();
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
switch (*c)
{
case '\'':
return ScanCharacterLiteral(Token, c, str, FALSE);
2021-03-22 18:19:39 +04:30
case '"':
do
{
*c = sgetc(str);
2020-12-03 18:32:11 +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
//
// An unterminated string literal would otherwise spin here forever,
// since sgetc() keeps returning EOF without consuming any input
//
if ((int)*c == EOF)
{
Token->Type = UNKNOWN;
return Token;
}
2021-03-22 18:19:39 +04:30
if (*c == '\\')
{
*c = sgetc(str);
if (*c == 'n')
{
AppendByte(Token, '\n');
2021-03-22 18:19:39 +04:30
continue;
}
if (*c == '\\')
{
AppendByte(Token, '\\');
2021-03-22 18:19:39 +04:30
continue;
}
else if (*c == 't')
{
AppendByte(Token, '\t');
2021-03-22 18:19:39 +04:30
continue;
}
else if (*c == 'x')
{
char ByteString[] = "000";
INT Len = (INT)strlen(ByteString);
int i = 0;
for (; i < Len; i++)
{
*c = sgetc(str);
if (!IsHex(*c))
break;
RotateLeftStringOnce(ByteString);
ByteString[Len - 1] = *c;
}
if (i == 0 || i == 3)
{
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
else
{
InputIdx--;
CHAR Num = (CHAR)strtol(ByteString, NULL, 16);
AppendByte(Token, Num);
}
}
2021-03-22 18:19:39 +04:30
else if (*c == '"')
{
AppendByte(Token, '"');
2021-03-22 18:19:39 +04:30
continue;
}
else
{
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
}
else if (*c == '"')
{
break;
}
else
{
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
}
} while (1);
2020-12-03 18:32:11 +03:30
Token->Len++;
2021-03-22 18:19:39 +04:30
Token->Type = STRING;
*c = sgetc(str);
return Token;
2021-08-20 22:36:25 +04:30
case '~':
strcpy(Token->Value, "~");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
2020-12-03 18:32:11 +03:30
2021-03-22 18:19:39 +04:30
case '+':
*c = sgetc(str);
if (*c == '+')
{
strcpy(Token->Value, "++");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else if (*c == '=')
{
strcpy(Token->Value, "+=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "+");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '-':
*c = sgetc(str);
2026-07-18 22:28:45 +08:00
if (*c == '>')
{
strcpy(Token->Value, "->");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else if (*c == '-')
2021-03-22 18:19:39 +04:30
{
strcpy(Token->Value, "--");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else if (*c == '=')
{
strcpy(Token->Value, "-=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "-");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '*':
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "*=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "*");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '>':
*c = sgetc(str);
if (*c == '>')
{
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, ">>=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, ">>");
Token->Type = SPECIAL_TOKEN;
return Token;
}
2021-03-22 18:19:39 +04:30
}
2021-04-11 23:31:36 +04:30
else if (*c == '=')
{
strcpy(Token->Value, ">=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
2021-03-22 18:19:39 +04:30
else
{
strcpy(Token->Value, ">");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '<':
*c = sgetc(str);
if (*c == '<')
{
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "<<=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "<<");
Token->Type = SPECIAL_TOKEN;
return Token;
}
2021-03-22 18:19:39 +04:30
}
2021-04-11 23:31:36 +04:30
else if (*c == '=')
{
strcpy(Token->Value, "<=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
2021-03-22 18:19:39 +04:30
else
{
strcpy(Token->Value, "<");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '/':
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "/=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
2020-12-03 18:32:11 +03:30
2021-03-22 18:19:39 +04:30
return Token;
}
else if (*c == '/')
{
do
{
*c = sgetc(str);
2021-12-03 17:38:18 +03:00
} while (*c != '\n' && (int)*c != EOF);
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
Token->Type = COMMENT;
*c = sgetc(str);
return Token;
}
else if (*c == '*')
{
do
{
*c = sgetc(str);
if (*c == '*')
{
*c = sgetc(str);
if (*c == '/')
{
Token->Type = COMMENT;
*c = sgetc(str);
return Token;
}
}
2021-12-03 17:38:18 +03:00
if ((int)*c == EOF)
2021-03-22 18:19:39 +04:30
break;
} while (1);
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "/");
Token->Type = SPECIAL_TOKEN;
return Token;
}
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case '=':
2021-04-11 23:31:36 +04:30
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "==");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "=");
Token->Type = SPECIAL_TOKEN;
return Token;
}
case '!':
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "!=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "!");
Token->Type = SPECIAL_TOKEN;
2021-04-11 23:31:36 +04:30
return Token;
}
2021-03-22 18:19:39 +04:30
case '%':
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "%=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "%");
Token->Type = SPECIAL_TOKEN;
}
2021-03-22 18:19:39 +04:30
return Token;
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case ',':
strcpy(Token->Value, ",");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case ';':
strcpy(Token->Value, ";");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case ':':
strcpy(Token->Value, ":");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case '(':
strcpy(Token->Value, "(");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
case ')':
strcpy(Token->Value, ")");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
case '{':
strcpy(Token->Value, "{");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
case '}':
strcpy(Token->Value, "}");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
2025-10-21 16:46:49 +08:00
case '[':
strcpy(Token->Value, "[");
Token->Type = SPECIAL_TOKEN;
2025-10-21 12:56:21 +02:00
*c = sgetc(str);
2025-10-21 16:46:49 +08:00
return Token;
case ']':
strcpy(Token->Value, "]");
Token->Type = SPECIAL_TOKEN;
2025-10-21 12:56:21 +02:00
*c = sgetc(str);
2025-10-21 16:46:49 +08:00
return Token;
2021-03-22 18:19:39 +04:30
case '|':
2021-04-11 23:31:36 +04:30
*c = sgetc(str);
if (*c == '|')
{
strcpy(Token->Value, "||");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else if (*c == '=')
{
strcpy(Token->Value, "|=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
2021-04-11 23:31:36 +04:30
else
{
strcpy(Token->Value, "|");
Token->Type = SPECIAL_TOKEN;
return Token;
}
2021-03-22 18:19:39 +04:30
case '&':
2021-04-11 23:31:36 +04:30
*c = sgetc(str);
if (*c == '&')
{
strcpy(Token->Value, "&&");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else if (*c == '=')
{
strcpy(Token->Value, "&=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
2021-04-11 23:31:36 +04:30
else
{
strcpy(Token->Value, "&");
Token->Type = SPECIAL_TOKEN;
return Token;
}
2021-03-22 18:19:39 +04:30
case '^':
*c = sgetc(str);
if (*c == '=')
{
strcpy(Token->Value, "^=");
Token->Type = SPECIAL_TOKEN;
*c = sgetc(str);
return Token;
}
else
{
strcpy(Token->Value, "^");
Token->Type = SPECIAL_TOKEN;
}
2021-03-22 18:19:39 +04:30
return Token;
case '@':
*c = sgetc(str);
if (IsLetter(*c))
{
while (IsLetter(*c) || IsDecimal(*c) || IsUnderscore(*c))
2021-03-22 18:19:39 +04:30
{
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
if (RegisterToInt(Token->Value) != INVALID)
{
Token->Type = REGISTER;
}
else
{
Token->Type = UNKNOWN;
}
2021-03-22 18:19:39 +04:30
return Token;
}
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case '$':
*c = sgetc(str);
if (IsLetter(*c))
{
//
// Append valid characters for pseudo registers' name
//
while (IsLetter(*c) || IsDecimal(*c) || *c == '_')
2021-03-22 18:19:39 +04:30
{
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
if (PseudoRegToInt(Token->Value) != INVALID)
{
Token->Type = PSEUDO_REGISTER;
}
else
{
Token->Type = UNKNOWN;
}
2021-03-22 18:19:39 +04:30
return Token;
}
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case '.':
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
2026-07-20 15:03:45 +08:00
if (IsDecimal(*c))
{
do
{
AppendByte(Token, *c);
*c = sgetc(str);
} while (IsDecimal(*c));
Token->Type = FLOAT_LITERAL;
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_DOUBLE;
return Token;
}
if (IsLetter(*c) || IsHex(*c) || (*c == '_') || (*c == '!'))
2021-03-22 18:19:39 +04:30
{
do
{
AppendByte(Token, *c);
*c = sgetc(str);
} while (IsLetter(*c) || IsHex(*c) || (*c == '_') || (*c == '!'));
BOOLEAN WasFound = FALSE;
2022-05-18 20:08:18 +09:00
BOOLEAN HasBang = strstr(Token->Value, "!") != 0;
UINT64 Address = 0;
if (HasBang)
{
Address = ScriptEngineConvertNameToAddress(Token->Value, &WasFound);
}
if (WasFound)
{
RemoveToken(&Token);
char HexStr[20] = {0};
sprintf(HexStr, "%llx", Address);
Token = NewToken(HEX, HexStr);
}
else
{
if (HasBang)
{
Token->Type = UNKNOWN;
return Token;
}
else
{
if (GetGlobalIdentifierVal(Token) != -1)
{
Token->Type = GLOBAL_ID;
Token->VariableType = GetGlobalIdentifierVariableType(Token);
Token->IsImplicitType = GetGlobalIdentifierIsImplicitType(Token);
}
else
{
Token->Type = GLOBAL_UNRESOLVED_ID;
}
}
}
2021-03-22 18:19:39 +04:30
}
else
{
Token->Type = UNKNOWN;
return Token;
2021-03-22 18:19:39 +04:30
}
return Token;
2020-10-24 12:19:10 +03:30
2026-01-06 16:38:22 +08:00
case '#':
do
{
if (*c != '`')
AppendByte(Token, *c);
*c = sgetc(str);
} while (IsLetter(*c) || IsHex(*c) || (*c == '_') || (*c == '!'));
if (IsKeyword(Token->Value))
{
Token->Type = KEYWORD;
}
else
{
Token->Type = UNKNOWN;
}
return Token;
2021-03-22 18:19:39 +04:30
case ' ':
case '\t':
case '\r':
2021-03-22 18:19:39 +04:30
strcpy(Token->Value, "");
Token->Type = WHITE_SPACE;
*c = sgetc(str);
return Token;
case '\n':
strcpy(Token->Value, "\n");
2021-03-22 18:19:39 +04:30
Token->Type = WHITE_SPACE;
*c = sgetc(str);
return Token;
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
case '0':
*c = sgetc(str);
if (*c == 'x')
{
*c = sgetc(str);
while (IsHex(*c) || *c == '`')
{
if (*c != '`')
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
Token->Type = HEX;
return Token;
}
else if (*c == 'o')
{
*c = sgetc(str);
while (IsOctal(*c) || *c == '`')
{
if (*c != '`')
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
Token->Type = OCTAL;
return Token;
}
else if (*c == 'n')
{
*c = sgetc(str);
while (IsDecimal(*c) || *c == '`')
{
if (*c != '`')
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
Token->Type = DECIMAL;
return Token;
}
else if (*c == 'y')
{
*c = sgetc(str);
while (IsBinary(*c) || *c == '`')
{
if (*c != '`')
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
}
Token->Type = BINARY;
return Token;
}
2020-10-24 12:19:10 +03:30
2026-07-20 15:03:45 +08:00
else if (*c == '.')
{
AppendByte(Token, '0');
AppendByte(Token, '.');
*c = sgetc(str);
while (IsDecimal(*c))
{
AppendByte(Token, *c);
*c = sgetc(str);
}
Token->Type = FLOAT_LITERAL;
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_DOUBLE;
return Token;
}
2021-03-22 18:19:39 +04:30
else if (IsHex(*c))
{
do
{
if (*c != '`')
AppendByte(Token, *c);
2021-03-22 18:19:39 +04:30
*c = sgetc(str);
} while (IsHex(*c) || *c == '`');
Token->Type = HEX;
return Token;
}
else
{
strcpy(Token->Value, "0");
Token->Type = HEX;
return Token;
}
2020-10-24 12:19:10 +03:30
case 'L':
if (*(str + InputIdx) == '\'')
{
InputIdx++;
return ScanCharacterLiteral(Token, c, str, TRUE);
}
if (*(str + InputIdx) == '"')
2021-03-22 18:19:39 +04:30
{
InputIdx++;
2021-03-22 18:19:39 +04:30
do
{
*c = sgetc(str);
2022-01-03 22:26:55 +09:00
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 unterminated wide string literal would otherwise spin here
// forever, since sgetc() keeps returning EOF without consuming input
//
if ((int)*c == EOF)
{
Token->Type = UNKNOWN;
return Token;
}
if (*c == '\\')
2021-03-22 18:19:39 +04:30
{
*c = sgetc(str);
if (*c == 'n')
{
AppendWchar(Token, (UINT16)'\n');
continue;
}
if (*c == '\\')
{
AppendWchar(Token, (UINT16)'\\');
continue;
}
else if (*c == 't')
{
AppendWchar(Token, (UINT16)'\t');
continue;
}
else if (*c == 'x')
{
char ByteString[] = "00000";
INT Len = (INT)strlen(ByteString);
int i = 0;
for (; i < Len; i++)
{
*c = sgetc(str);
if (!IsHex(*c))
break;
RotateLeftStringOnce(ByteString);
ByteString[Len - 1] = *c;
}
if (i == 0 || i == 5)
{
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
else
{
InputIdx--;
UINT16 Num = (UINT16)strtol(ByteString, NULL, 16);
AppendWchar(Token, Num);
}
}
else if (*c == '"')
{
AppendWchar(Token, (UINT16)'"');
continue;
}
else
{
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
2021-03-22 18:19:39 +04:30
}
else if (*c == '"')
2021-03-22 18:19:39 +04:30
{
break;
}
else
{
AppendWchar(Token, (UINT16)(UINT8)*c);
2021-03-22 18:19:39 +04:30
}
} while (1);
Token->Len += 2;
Token->Type = WSTRING;
*c = sgetc(str);
return Token;
}
default:
if (*c >= '0' && *c <= '9')
{
2026-07-20 15:03:45 +08:00
BOOLEAN HasOnlyDecimalDigits = TRUE;
do
2021-03-22 18:19:39 +04:30
{
if (*c != '`')
2026-07-20 15:03:45 +08:00
{
AppendByte(Token, *c);
2026-07-20 15:03:45 +08:00
if (!IsDecimal(*c))
{
HasOnlyDecimalDigits = FALSE;
}
}
*c = sgetc(str);
} while (IsHex(*c) || *c == '`');
2026-07-20 15:03:45 +08:00
if (*c == '.' && HasOnlyDecimalDigits)
{
AppendByte(Token, '.');
*c = sgetc(str);
while (IsDecimal(*c))
{
AppendByte(Token, *c);
*c = sgetc(str);
}
Token->Type = FLOAT_LITERAL;
Token->VariableType = (VARIABLE_TYPE *)VARIABLE_TYPE_DOUBLE;
return Token;
}
Token->Type = HEX;
return Token;
}
else if ((*c >= 'a' && *c <= 'f') || (*c >= 'A' && *c <= 'F') || (*c == '_') || (*c == '!'))
{
UINT8 NotHex = 0;
do
{
if (*c != '`')
AppendByte(Token, *c);
*c = sgetc(str);
if (IsHex(*c) || *c == '`' || *c == '_')
2021-03-22 18:19:39 +04:30
{
// Nothing
}
else if ((*c >= 'G' && *c <= 'Z') || (*c >= 'g' && *c <= 'z'))
{
NotHex = 1;
break;
}
else
{
break;
}
} while (1);
if (NotHex)
{
do
2021-03-22 18:19:39 +04:30
{
if (*c != '`')
AppendByte(Token, *c);
*c = sgetc(str);
} while (IsLetter(*c) || IsHex(*c) || (*c == '_') || (*c == '!'));
if (IsKeyword(Token->Value))
2021-03-22 18:19:39 +04:30
{
Token->Type = KEYWORD;
}
else if (IsRegister(Token->Value))
{
Token->Type = REGISTER;
2021-03-22 18:19:39 +04:30
}
2024-07-25 01:52:58 +08:00
else if (IsVariableType(Token->Value))
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
else if ((Token->VariableType = FindTypedefType(Token->Value)) != NULL)
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
2021-03-22 18:19:39 +04:30
else
{
BOOLEAN WasFound = FALSE;
BOOLEAN HasBang = strstr(Token->Value, "!") != 0;
UINT64 Address = 0;
if (HasBang)
{
Address = ScriptEngineConvertNameToAddress(Token->Value, &WasFound);
}
if (WasFound)
2021-06-11 16:18:27 +04:30
{
RemoveToken(&Token);
char str[20] = {0};
sprintf(str, "%llx", Address);
Token = NewToken(HEX, str);
2021-06-11 16:18:27 +04:30
}
else
2021-06-11 16:18:27 +04:30
{
2022-05-18 20:08:18 +09:00
if (HasBang)
2021-06-27 19:48:25 +04:30
{
Token->Type = UNKNOWN;
return Token;
2021-06-27 19:48:25 +04:30
}
else
{
if (GetUserDefinedFunctionNode(Token))
{
Token->Type = FUNCTION_ID;
}
else if (GetFunctionParameterIdentifier(Token) != -1)
2021-06-27 19:48:25 +04:30
{
Token->Type = FUNCTION_PARAMETER_ID;
Token->VariableType = GetFunctionParameterVariableType(Token);
Token->VariableMemoryIdx = GetFunctionParameterMemoryIndex(Token);
Token->Len = GetFunctionParameterSlotCount(Token);
Token->AddressSpace = SCRIPT_ENGINE_ADDRESS_SPACE_LOCAL;
2021-06-27 19:48:25 +04:30
}
else if (GetLocalIdentifierVal(Token) != -1)
{
Token->Type = LOCAL_ID;
Token->VariableType = GetLocalIdentifierVariableType(Token);
Token->IsImplicitType = GetLocalIdentifierIsImplicitType(Token);
}
else
2021-06-27 19:48:25 +04:30
{
Token->Type = LOCAL_UNRESOLVED_ID;
2021-06-27 19:48:25 +04:30
}
}
2021-06-11 16:18:27 +04:30
}
2021-03-22 18:19:39 +04:30
}
return Token;
2021-03-22 18:19:39 +04:30
}
else
2021-03-22 18:19:39 +04:30
{
if (IsKeyword(Token->Value))
{
Token->Type = KEYWORD;
}
else if (IsRegister(Token->Value))
2021-03-22 18:19:39 +04:30
{
Token->Type = REGISTER;
}
2024-07-25 01:52:58 +08:00
else if (IsVariableType(Token->Value))
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
else if ((Token->VariableType = FindTypedefType(Token->Value)) != NULL)
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
else if (IsId(Token->Value))
2021-03-22 18:19:39 +04:30
{
2021-06-11 16:18:27 +04:30
BOOLEAN WasFound = FALSE;
BOOLEAN HasBang = strstr(Token->Value, "!") != 0;
UINT64 Address = 0;
if (HasBang)
{
Address = ScriptEngineConvertNameToAddress(Token->Value, &WasFound);
}
2021-06-11 16:18:27 +04:30
if (WasFound)
{
2022-05-19 20:16:57 +09:00
RemoveToken(&Token);
char HexStr[20] = {0};
sprintf(HexStr, "%llx", Address);
Token = NewToken(HEX, HexStr);
2021-06-11 16:18:27 +04:30
}
else
{
2022-05-18 20:08:18 +09:00
if (HasBang)
2021-06-27 19:48:25 +04:30
{
Token->Type = UNKNOWN;
return Token;
}
else
{
if (GetUserDefinedFunctionNode(Token))
{
Token->Type = FUNCTION_ID;
}
else if (GetFunctionParameterIdentifier(Token) != -1)
2021-06-27 19:48:25 +04:30
{
Token->Type = FUNCTION_PARAMETER_ID;
Token->VariableType = GetFunctionParameterVariableType(Token);
Token->VariableMemoryIdx = GetFunctionParameterMemoryIndex(Token);
Token->Len = GetFunctionParameterSlotCount(Token);
Token->AddressSpace = SCRIPT_ENGINE_ADDRESS_SPACE_LOCAL;
2021-06-27 19:48:25 +04:30
}
else if (GetLocalIdentifierVal(Token) != -1)
{
Token->Type = LOCAL_ID;
Token->VariableType = GetLocalIdentifierVariableType(Token);
Token->IsImplicitType = GetLocalIdentifierIsImplicitType(Token);
}
2021-06-27 19:48:25 +04:30
else
{
Token->Type = LOCAL_UNRESOLVED_ID;
2021-06-27 19:48:25 +04:30
}
Token->VariableType;
2021-06-27 19:48:25 +04:30
}
2021-06-11 16:18:27 +04:30
}
2021-03-22 18:19:39 +04:30
}
else
{
Token->Type = HEX;
}
2021-03-22 18:19:39 +04:30
return Token;
}
}
else if ((*c >= 'G' && *c <= 'Z') || (*c >= 'g' && *c <= 'z') || (*c == '_') || (*c == '!'))
{
do
{
if (*c != '`')
AppendByte(Token, *c);
*c = sgetc(str);
} while (IsLetter(*c) || IsHex(*c) || (*c == '_') || (*c == '!'));
if (IsKeyword(Token->Value))
{
Token->Type = KEYWORD;
}
else if (IsRegister(Token->Value))
{
Token->Type = REGISTER;
}
2024-07-25 01:52:58 +08:00
else if (IsVariableType(Token->Value))
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
else if ((Token->VariableType = FindTypedefType(Token->Value)) != NULL)
{
Token->Type = SCRIPT_VARIABLE_TYPE;
}
else
{
BOOLEAN WasFound = FALSE;
BOOLEAN HasBang = strstr(Token->Value, "!") != 0;
UINT64 Address = 0;
if (HasBang)
{
Address = ScriptEngineConvertNameToAddress(Token->Value, &WasFound);
}
if (WasFound)
{
RemoveToken(&Token);
char HexStr[20] = {0};
sprintf(HexStr, "%llx", Address);
Token = NewToken(HEX, HexStr);
}
else
{
if (HasBang)
{
Token->Type = UNKNOWN;
return Token;
}
else
{
if (GetUserDefinedFunctionNode(Token))
{
Token->Type = FUNCTION_ID;
}
else if (GetFunctionParameterIdentifier(Token) != -1)
{
Token->Type = FUNCTION_PARAMETER_ID;
Token->VariableType = GetFunctionParameterVariableType(Token);
Token->VariableMemoryIdx = GetFunctionParameterMemoryIndex(Token);
Token->Len = GetFunctionParameterSlotCount(Token);
Token->AddressSpace = SCRIPT_ENGINE_ADDRESS_SPACE_LOCAL;
}
else if (GetLocalIdentifierVal(Token) != -1)
{
Token->Type = LOCAL_ID;
Token->VariableType = GetLocalIdentifierVariableType(Token);
Token->IsImplicitType = GetLocalIdentifierIsImplicitType(Token);
}
else
{
Token->Type = LOCAL_UNRESOLVED_ID;
}
}
}
}
return Token;
}
2020-10-24 12:19:10 +03:30
2021-03-22 18:19:39 +04:30
Token->Type = UNKNOWN;
*c = sgetc(str);
return Token;
}
return Token;
2020-10-20 19:35:15 +03:30
}
2022-05-10 15:08:24 +04:30
2020-10-20 19:35:15 +03:30
/**
2022-05-18 18:17:30 +04:30
* @brief Perform scanning the script engine
2023-01-18 20:23:40 +09:00
*
* @param str
* @param c
* @return PSCRIPT_ENGINE_TOKEN
2022-05-10 15:08:24 +04:30
*/
PSCRIPT_ENGINE_TOKEN
2021-03-22 18:19:39 +04:30
Scan(char * str, char * c)
2020-10-20 19:35:15 +03:30
{
static BOOLEAN ReturnEndOfString;
PSCRIPT_ENGINE_TOKEN Token;
2020-10-24 12:19:10 +03:30
2022-05-19 20:16:57 +09:00
if (InputIdx <= 1)
{
2026-07-18 22:28:45 +08:00
ReturnEndOfString = FALSE;
PreviousTokenCanEndExpression = FALSE;
}
if (ReturnEndOfString)
{
Token = NewToken(END_OF_STACK, "$");
return Token;
}
if (str[InputIdx - 1] == '\0')
{
}
2021-03-22 18:19:39 +04:30
while (1)
{
CurrentTokenIdx = InputIdx - 1;
2026-07-18 22:28:45 +08:00
if (*c == '.' && PreviousTokenCanEndExpression)
{
Token = NewToken(SPECIAL_TOKEN, ".");
*c = sgetc(str);
}
else
{
Token = GetToken(c, str);
}
2020-10-24 12:19:10 +03:30
if ((int)*c == EOF)
2021-03-22 18:19:39 +04:30
{
ReturnEndOfString = TRUE;
2021-03-22 18:19:39 +04:30
}
2022-05-18 23:44:03 +04:30
if (Token->Type == WHITE_SPACE)
2021-03-22 18:19:39 +04:30
{
if (!strcmp(Token->Value, "\n"))
2021-03-22 18:19:39 +04:30
{
CurrentLine++;
CurrentLineIdx = InputIdx;
}
2022-05-19 20:16:57 +09:00
RemoveToken(&Token);
2022-05-19 22:02:19 +09:00
if (ReturnEndOfString)
{
Token = NewToken(END_OF_STACK, "$");
return Token;
}
2021-03-22 18:19:39 +04:30
continue;
}
else if (Token->Type == COMMENT)
{
2022-05-19 20:16:57 +09:00
RemoveToken(&Token);
2022-05-19 22:02:19 +09:00
if (ReturnEndOfString)
{
Token = NewToken(END_OF_STACK, "$");
return Token;
}
2021-03-22 18:19:39 +04:30
continue;
}
2026-07-18 22:28:45 +08:00
PreviousTokenCanEndExpression =
Token->Type == GLOBAL_ID || Token->Type == GLOBAL_UNRESOLVED_ID ||
Token->Type == LOCAL_ID || Token->Type == LOCAL_UNRESOLVED_ID ||
Token->Type == FUNCTION_PARAMETER_ID || Token->Type == REGISTER ||
Token->Type == PSEUDO_REGISTER || Token->Type == HEX ||
Token->Type == DECIMAL || Token->Type == OCTAL || Token->Type == BINARY ||
2026-07-20 15:03:45 +08:00
Token->Type == FLOAT_LITERAL ||
2026-07-18 22:28:45 +08:00
(Token->Type == SPECIAL_TOKEN &&
(!strcmp(Token->Value, ")") || !strcmp(Token->Value, "]")));
2021-03-22 18:19:39 +04:30
return Token;
}
2020-10-20 19:35:15 +03:30
}
2020-10-28 08:31:51 +03:30
/**
* @brief Returns the next character in the input string
2023-01-18 20:23:40 +09:00
*
* @param str
* @return CHAR the next character at the current position in the string
2023-01-18 20:23:40 +09:00
*/
2021-03-22 18:19:39 +04:30
char
sgetc(char * str)
2020-10-20 19:35:15 +03:30
{
2021-03-22 18:19:39 +04:30
char c = str[InputIdx];
2021-03-22 18:19:39 +04:30
if (c)
{
InputIdx++;
return c;
}
else
{
return EOF;
}
2020-11-04 15:50:14 +03:30
}
2022-05-10 15:08:24 +04:30
/**
2022-05-18 18:17:30 +04:30
* @brief Check whether a string is a keyword or not
2023-01-18 20:23:40 +09:00
*
* @param str
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsKeyword(char * str)
2020-11-04 15:50:14 +03:30
{
2021-04-11 23:31:36 +04:30
int n = KEYWORD_LIST_LENGTH;
for (int i = 0; i < n; i++)
{
if (!strcmp(str, KeywordList[i]))
{
return 1;
}
}
n = TERMINAL_COUNT;
for (int i = 0; i < n; i++)
{
if (!strcmp(str, TerminalMap[i]))
{
return 1;
}
}
2021-04-10 19:29:21 +04:30
2021-04-11 23:31:36 +04:30
return 0;
}
2021-03-08 19:26:56 +03:30
2022-05-10 15:08:24 +04:30
/**
2022-05-18 18:17:30 +04:30
* @brief Check if string is register or not
2023-01-18 20:23:40 +09:00
*
* @param str
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsRegister(char * str)
2021-03-08 19:26:56 +03:30
{
if (RegisterToInt(str) == INVALID)
return 0;
return 1;
2021-03-08 19:26:56 +03:30
}
2022-05-10 15:08:24 +04:30
2024-07-25 01:52:58 +08:00
/**
* @brief Check if string is variable type or not
*
* @param str
* @return char
*/
char
IsVariableType(char * str)
{
for (int i = 0; i < SCRIPT_VARIABLE_TYPE_LIST_LENGTH; i++)
{
if (!strcmp(str, ScriptVariableTypeList[i]))
{
return 1;
}
}
return 0;
}
2022-05-10 15:08:24 +04:30
/**
* @brief Check if string is Id or not
2023-01-18 20:23:40 +09:00
*
* @param str
* @return char
2022-05-10 15:08:24 +04:30
*/
2021-03-22 18:19:39 +04:30
char
IsId(char * str)
{
2021-03-22 18:19:39 +04:30
// TODO: Check the str is a id or not
return 0;
2021-04-11 23:31:36 +04:30
}