mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-21 21:58:50 -05:00
Source: Avoid out-of-range inputs to std::isspace()
`isspace` takes `int` but documents that the value must be representable
by `unsigned char`, or be EOF. Use a wrapper to cast to `unsigned char`
to avoid sign extension while converting to `int`. This generalizes the
fix from commit 5e8c176e2a (cmExecuteProcessCommand: Cast c to unsigned
char before cast to int, 2024-01-05) to other `isspace` call sites.
This was detected by assertions in the MSVC standard library while
processing UTF-8 text.
Issue: #25561
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||||
#include "cmCTestGIT.h"
|
#include "cmCTestGIT.h"
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@@ -414,14 +413,14 @@ protected:
|
|||||||
|
|
||||||
const char* ConsumeSpace(const char* c)
|
const char* ConsumeSpace(const char* c)
|
||||||
{
|
{
|
||||||
while (*c && isspace(*c)) {
|
while (*c && cmIsSpace(*c)) {
|
||||||
++c;
|
++c;
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
const char* ConsumeField(const char* c)
|
const char* ConsumeField(const char* c)
|
||||||
{
|
{
|
||||||
while (*c && !isspace(*c)) {
|
while (*c && !cmIsSpace(*c)) {
|
||||||
++c;
|
++c;
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
@@ -481,7 +480,7 @@ private:
|
|||||||
{
|
{
|
||||||
// Person Name <person@domain.com> 1234567890 +0000
|
// Person Name <person@domain.com> 1234567890 +0000
|
||||||
const char* c = str;
|
const char* c = str;
|
||||||
while (*c && isspace(*c)) {
|
while (*c && cmIsSpace(*c)) {
|
||||||
++c;
|
++c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +489,7 @@ private:
|
|||||||
++c;
|
++c;
|
||||||
}
|
}
|
||||||
const char* name_last = c;
|
const char* name_last = c;
|
||||||
while (name_last != name_first && isspace(*(name_last - 1))) {
|
while (name_last != name_first && cmIsSpace(*(name_last - 1))) {
|
||||||
--name_last;
|
--name_last;
|
||||||
}
|
}
|
||||||
person.Name.assign(name_first, name_last - name_first);
|
person.Name.assign(name_first, name_last - name_first);
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
|
|||||||
if (std::isalpha(ch) || ch == '_') {
|
if (std::isalpha(ch) || ch == '_') {
|
||||||
key += ch;
|
key += ch;
|
||||||
state = PARSE_KEY;
|
state = PARSE_KEY;
|
||||||
} else if (!std::isspace(ch)) {
|
} else if (!cmIsSpace(ch)) {
|
||||||
state = IGNORE_REST;
|
state = IGNORE_REST;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -238,7 +238,7 @@ cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PARSE_VALUE:
|
case PARSE_VALUE:
|
||||||
if (ch == '#' || std::isspace(ch)) {
|
if (ch == '#' || cmIsSpace(ch)) {
|
||||||
state = IGNORE_REST;
|
state = IGNORE_REST;
|
||||||
} else {
|
} else {
|
||||||
value += ch;
|
value += ch;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||||
#include "cmExecuteProcessCommand.h"
|
#include "cmExecuteProcessCommand.h"
|
||||||
|
|
||||||
#include <cctype> /* isspace */
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -35,11 +34,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
bool cmExecuteProcessCommandIsWhitespace(char c)
|
bool cmExecuteProcessCommandIsWhitespace(char c)
|
||||||
{
|
{
|
||||||
// isspace takes 'int' but documents that the value must be representable
|
return (cmIsSpace(c) || c == '\n' || c == '\r');
|
||||||
// by 'unsigned char', or EOF. Cast to 'unsigned char' to avoid sign
|
|
||||||
// extension while casting to 'int'.
|
|
||||||
return (isspace(static_cast<int>(static_cast<unsigned char>(c))) ||
|
|
||||||
c == '\n' || c == '\r');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmExecuteProcessCommandFixText(std::vector<char>& output,
|
void cmExecuteProcessCommandFixText(std::vector<char>& output,
|
||||||
|
|||||||
@@ -1438,8 +1438,8 @@ static void s_RemoveDefineFlag(std::string const& flag, std::string& dflags)
|
|||||||
for (std::string::size_type lpos = dflags.find(flag, 0);
|
for (std::string::size_type lpos = dflags.find(flag, 0);
|
||||||
lpos != std::string::npos; lpos = dflags.find(flag, lpos)) {
|
lpos != std::string::npos; lpos = dflags.find(flag, lpos)) {
|
||||||
std::string::size_type rpos = lpos + len;
|
std::string::size_type rpos = lpos + len;
|
||||||
if ((lpos <= 0 || isspace(dflags[lpos - 1])) &&
|
if ((lpos <= 0 || cmIsSpace(dflags[lpos - 1])) &&
|
||||||
(rpos >= dflags.size() || isspace(dflags[rpos]))) {
|
(rpos >= dflags.size() || cmIsSpace(dflags[rpos]))) {
|
||||||
dflags.erase(lpos, len);
|
dflags.erase(lpos, len);
|
||||||
} else {
|
} else {
|
||||||
++lpos;
|
++lpos;
|
||||||
|
|||||||
+2
-3
@@ -3,7 +3,6 @@
|
|||||||
#include "cmRST.h"
|
#include "cmRST.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -159,7 +158,7 @@ void cmRST::ProcessLine(std::string const& line)
|
|||||||
// A line starting in .. is an explicit markup start.
|
// A line starting in .. is an explicit markup start.
|
||||||
if (line == ".." ||
|
if (line == ".." ||
|
||||||
(line.size() >= 3 && line[0] == '.' && line[1] == '.' &&
|
(line.size() >= 3 && line[0] == '.' && line[1] == '.' &&
|
||||||
isspace(line[2]))) {
|
cmIsSpace(line[2]))) {
|
||||||
this->Reset();
|
this->Reset();
|
||||||
this->MarkupType =
|
this->MarkupType =
|
||||||
(line.find_first_not_of(" \t", 2) == std::string::npos ? Markup::Empty
|
(line.find_first_not_of(" \t", 2) == std::string::npos ? Markup::Empty
|
||||||
@@ -219,7 +218,7 @@ void cmRST::ProcessLine(std::string const& line)
|
|||||||
}
|
}
|
||||||
// Indented lines following an explicit markup start are explicit markup.
|
// Indented lines following an explicit markup start are explicit markup.
|
||||||
else if (this->MarkupType != Markup::None &&
|
else if (this->MarkupType != Markup::None &&
|
||||||
(line.empty() || isspace(line[0]))) {
|
(line.empty() || cmIsSpace(line[0]))) {
|
||||||
this->MarkupType = Markup::Normal;
|
this->MarkupType = Markup::Normal;
|
||||||
// Record markup lines if the start line was recorded.
|
// Record markup lines if the start line was recorded.
|
||||||
if (!this->MarkupLines.empty()) {
|
if (!this->MarkupLines.empty()) {
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ private:
|
|||||||
/** Returns true if the character @a ch is a whitespace character. **/
|
/** Returns true if the character @a ch is a whitespace character. **/
|
||||||
inline bool cmIsSpace(char ch)
|
inline bool cmIsSpace(char ch)
|
||||||
{
|
{
|
||||||
return ((ch & 0x80) == 0) && std::isspace(ch);
|
// isspace takes 'int' but documents that the value must be representable
|
||||||
|
// by 'unsigned char', or be EOF. Cast to 'unsigned char' to avoid sign
|
||||||
|
// extension while converting to 'int'.
|
||||||
|
return std::isspace(static_cast<unsigned char>(ch));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a string that has whitespace removed from the start and the end. */
|
/** Returns a string that has whitespace removed from the start and the end. */
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "cmStringCommand.h"
|
#include "cmStringCommand.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -660,7 +659,7 @@ bool HandleStripCommand(std::vector<std::string> const& args,
|
|||||||
const char* ptr = stringValue.c_str();
|
const char* ptr = stringValue.c_str();
|
||||||
size_t cc;
|
size_t cc;
|
||||||
for (cc = 0; cc < inStringLength; ++cc) {
|
for (cc = 0; cc < inStringLength; ++cc) {
|
||||||
if (!isspace(*ptr)) {
|
if (!cmIsSpace(*ptr)) {
|
||||||
if (startPos > inStringLength) {
|
if (startPos > inStringLength) {
|
||||||
startPos = cc;
|
startPos = cc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
|
|||||||
const char* c = command.c_str();
|
const char* c = command.c_str();
|
||||||
|
|
||||||
// Skip leading whitespace.
|
// Skip leading whitespace.
|
||||||
while (isspace(static_cast<unsigned char>(*c))) {
|
while (cmIsSpace(*c)) {
|
||||||
++c;
|
++c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
|
|||||||
in_double = true;
|
in_double = true;
|
||||||
} else if (*c == '\'') {
|
} else if (*c == '\'') {
|
||||||
in_single = true;
|
in_single = true;
|
||||||
} else if (isspace(static_cast<unsigned char>(*c))) {
|
} else if (cmIsSpace(*c)) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
program += *c;
|
program += *c;
|
||||||
|
|||||||
Reference in New Issue
Block a user