[flang-rt] Fix EXECUTE_COMMAND_LINE() on Windows (#184875)
Detect cmd.exe special status code 9009 that indicates "command not found" condition. Crash the process if "command not found" detected when CMDSTAT was not specified.
This commit is contained in:
parent
c9555f6675
commit
008dc8b5bf
@ -111,7 +111,17 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
|
||||
// On WIN32 API std::system() returns exit status directly. On other OS'es,
|
||||
// special status codes are handled below.
|
||||
std::int64_t exitStatusVal{status};
|
||||
#ifndef _WIN32
|
||||
#ifdef _WIN32
|
||||
if (status == 9009) {
|
||||
// cmd.exe returns status code 9009 for "command not found" error
|
||||
if (!cmdstat) {
|
||||
terminator.Crash("Command not found.");
|
||||
} else {
|
||||
StoreIntToDescriptor(cmdstat, COMMAND_NOT_FOUND_ERR, terminator);
|
||||
CheckAndCopyCharsToDescriptor(cmdmsg, "Command not found.");
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
#if defined(WIFSIGNALED) && defined(WTERMSIG)
|
||||
if (WIFSIGNALED(status)) {
|
||||
@ -195,9 +205,36 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
|
||||
RUNTIME_CHECK(terminator, IsValidCharDescriptor(cmdmsg));
|
||||
}
|
||||
|
||||
const char *cmd{newCmd};
|
||||
#ifdef _WIN32
|
||||
// Construct a string that looks like
|
||||
// "cmd.exe /v:on /c \"mycommand & exit /b !ERRORLEVEL!\""
|
||||
// Explanantion:
|
||||
// /v:on - turns delayed environment variable expansion on, so
|
||||
// variables written as !VAR! are expanded at execution time
|
||||
// instead of at parse time. This is required for !ERRORLEVEL!
|
||||
// to reflect the current error code at the moment exit runs.
|
||||
// exit /b !ERRORLEVEL! - exits the current cmd instance (/b) and
|
||||
// sets its process exit code to the current ERRORLEVEL value.
|
||||
// Because delayed expansion is on, !ERRORLEVEL! is evaluated at
|
||||
// execution time, so this cmd instance returns the same error
|
||||
// code as mycommand.
|
||||
// This allows cmd.exe to either return the exit code of mycommand, or
|
||||
// to return its own exit code to the caller. The code 9009 is used
|
||||
// by cmd.exe to indicate "not found" condition.
|
||||
const char prefix[]{"cmd.exe /v:on /c \""};
|
||||
const char suffix[]{" & exit /b !ERRORLEVEL!\""};
|
||||
const size_t newCmdWinLen{
|
||||
(sizeof(prefix) - 1) + std::strlen(newCmd) + (sizeof(suffix) - 1) + 1};
|
||||
char *newCmdWin{
|
||||
static_cast<char *>(AllocateMemoryOrCrash(terminator, newCmdWinLen))};
|
||||
std::snprintf(newCmdWin, newCmdWinLen, "%s%s%s", prefix, newCmd, suffix);
|
||||
cmd = newCmdWin;
|
||||
#endif
|
||||
|
||||
if (wait) {
|
||||
// either wait is not specified or wait is true: synchronous mode
|
||||
std::int64_t status{std::system(newCmd)};
|
||||
std::int64_t status{std::system(cmd)};
|
||||
std::int64_t exitStatusVal{
|
||||
TerminationCheck(status, cmdstat, cmdmsg, terminator)};
|
||||
// If sync, assigned processor-dependent exit status. Otherwise unchanged
|
||||
@ -211,13 +248,6 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// add "cmd.exe /c " to the beginning of command
|
||||
const char *prefix{"cmd.exe /c "};
|
||||
char *newCmdWin{static_cast<char *>(AllocateMemoryOrCrash(
|
||||
terminator, std::strlen(prefix) + std::strlen(newCmd) + 1))};
|
||||
std::strcpy(newCmdWin, prefix);
|
||||
std::strcat(newCmdWin, newCmd);
|
||||
|
||||
// Convert the char to wide char
|
||||
const size_t sizeNeeded{mbstowcs(NULL, newCmdWin, 0) + 1};
|
||||
wchar_t *wcmd{static_cast<wchar_t *>(
|
||||
@ -225,7 +255,6 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
|
||||
if (std::mbstowcs(wcmd, newCmdWin, sizeNeeded) == static_cast<size_t>(-1)) {
|
||||
terminator.Crash("Char to wide char failed for newCmd");
|
||||
}
|
||||
FreeMemory(newCmdWin);
|
||||
|
||||
if (CreateProcessW(nullptr, wcmd, nullptr, nullptr, FALSE, 0, nullptr,
|
||||
nullptr, &si, &pi)) {
|
||||
@ -278,6 +307,11 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
FreeMemory(newCmdWin);
|
||||
#endif
|
||||
|
||||
// Deallocate memory if EnsureNullTerminated dynamically allocated memory
|
||||
if (newCmd != command.OffsetElement()) {
|
||||
FreeMemory(newCmd);
|
||||
|
||||
@ -365,9 +365,9 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
|
||||
RTNAME(ExecuteCommandLine)
|
||||
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
|
||||
#ifdef _WIN32
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
|
||||
CheckDescriptorEqStr(cmdMsg.get(), "cmd msg buffer XXXXXXXX");
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 9009);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
|
||||
CheckDescriptorEqStr(cmdMsg.get(), "Command not found.");
|
||||
#else
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
|
||||
@ -394,9 +394,9 @@ TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
|
||||
RTNAME(ExecuteCommandLine)
|
||||
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
|
||||
#ifdef _WIN32
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
|
||||
CheckDescriptorEqStr(cmdMsg.get(), "unmodified buffer XXXXXXXXX");
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 9009);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
|
||||
CheckDescriptorEqStr(cmdMsg.get(), "Command not found.");
|
||||
#else
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
|
||||
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
|
||||
@ -412,7 +412,7 @@ TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
|
||||
#ifdef _WIN32
|
||||
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
|
||||
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
|
||||
"Invalid command quit with exit status code: 1");
|
||||
"Command not found.");
|
||||
#else
|
||||
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
|
||||
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
|
||||
@ -490,7 +490,7 @@ TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
|
||||
RTNAME(ExecuteCommandLine)
|
||||
(*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
|
||||
#ifdef _WIN32
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 9009);
|
||||
#else
|
||||
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user