Prevent integer overflow when computing texture sizes and allocating memory for them.
Correctly check for failed allocations when resizing std::vector PiperOrigin-RevId: 659488680 Change-Id: I7bf4c0b416e96eda2baec32b21be78456604f4b0
This commit is contained in:
committed by
Copybara-Service
parent
ff5d339e48
commit
6a8e28f0e9
@@ -22,8 +22,10 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <new>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -3423,6 +3425,9 @@ void mjCTexture::Builtin2D(void) {
|
|||||||
void mjCTexture::BuiltinCube(void) {
|
void mjCTexture::BuiltinCube(void) {
|
||||||
unsigned char RGB1[3], RGB2[3], RGBm[3], RGBi[3];
|
unsigned char RGB1[3], RGB2[3], RGBm[3], RGBi[3];
|
||||||
int w = width;
|
int w = width;
|
||||||
|
if (w > std::numeric_limits<int>::max() / w) {
|
||||||
|
throw mjCError(this, "Cube texture width is too large.");
|
||||||
|
}
|
||||||
int ww = width*width;
|
int ww = width*width;
|
||||||
|
|
||||||
// convert fixed colors
|
// convert fixed colors
|
||||||
@@ -3436,6 +3441,9 @@ void mjCTexture::BuiltinCube(void) {
|
|||||||
|
|
||||||
// gradient
|
// gradient
|
||||||
if (builtin == mjBUILTIN_GRADIENT) {
|
if (builtin == mjBUILTIN_GRADIENT) {
|
||||||
|
if (ww > std::numeric_limits<int>::max() / 18) {
|
||||||
|
throw mjCError(this, "Gradient texture width is too large.");
|
||||||
|
}
|
||||||
for (int r = 0; r < w; r++) {
|
for (int r = 0; r < w; r++) {
|
||||||
for (int c = 0; c < w; c++) {
|
for (int c = 0; c < w; c++) {
|
||||||
// compute normalized pixel coordinates
|
// compute normalized pixel coordinates
|
||||||
@@ -3677,12 +3685,17 @@ void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
|
|||||||
height = h;
|
height = h;
|
||||||
|
|
||||||
// allocate and copy data
|
// allocate and copy data
|
||||||
data.assign(nchannel*width*height, 0);
|
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||||
if (data.empty()) {
|
if (size >= std::numeric_limits<int>::max() / nchannel || size <= 0) {
|
||||||
|
throw mjCError(this, "Texture too large");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
data.assign(nchannel*size, 0);
|
||||||
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
|
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
|
||||||
(const char*)file_.c_str(), id);
|
(const char*)file_.c_str(), id);
|
||||||
}
|
}
|
||||||
memcpy(data.data(), image.data(), nchannel*width*height);
|
memcpy(data.data(), image.data(), nchannel*size);
|
||||||
image.clear();
|
image.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3712,12 +3725,20 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
|||||||
width = height = w;
|
width = height = w;
|
||||||
} else {
|
} else {
|
||||||
width = w/gridsize[1];
|
width = w/gridsize[1];
|
||||||
|
if (width >= std::numeric_limits<int>::max()/6) {
|
||||||
|
throw mjCError(this, "Invalid width of cube texture");
|
||||||
|
}
|
||||||
height = 6*width;
|
height = 6*width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate data
|
// allocate data
|
||||||
data.assign(3*width*height, 0);
|
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||||
if (data.empty()) {
|
if (size >= std::numeric_limits<int>::max() / 3 || size <= 0) {
|
||||||
|
throw mjCError(this, "Cube texture too large");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
data.assign(3*size, 0);
|
||||||
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this,
|
throw mjCError(this,
|
||||||
"Could not allocate memory for texture '%s' (id %d)",
|
"Could not allocate memory for texture '%s' (id %d)",
|
||||||
(const char*)file_.c_str(), id);
|
(const char*)file_.c_str(), id);
|
||||||
@@ -3816,9 +3837,17 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
// first file: set size and allocate data
|
// first file: set size and allocate data
|
||||||
if (data.empty()) {
|
if (data.empty()) {
|
||||||
width = w;
|
width = w;
|
||||||
|
if (width >= std::numeric_limits<int>::max()/6) {
|
||||||
|
throw mjCError(this, "Invalid width of builtin texture");
|
||||||
|
}
|
||||||
height = 6*width;
|
height = 6*width;
|
||||||
data.assign(3*width*height, 0);
|
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||||
if (data.empty()) {
|
if (size >= std::numeric_limits<int>::max() / 3 || size <= 0) {
|
||||||
|
throw mjCError(this, "PNG texture too large");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
data.assign(3*size, 0);
|
||||||
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture");
|
throw mjCError(this, "Could not allocate memory for texture");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3868,6 +3897,9 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
|||||||
|
|
||||||
// adjust height of cube texture
|
// adjust height of cube texture
|
||||||
if (type != mjTEXTURE_2D) {
|
if (type != mjTEXTURE_2D) {
|
||||||
|
if (width >= std::numeric_limits<int>::max()/6) {
|
||||||
|
throw mjCError(this, "Invalid width of builtin texture");
|
||||||
|
}
|
||||||
height = 6*width;
|
height = 6*width;
|
||||||
} else {
|
} else {
|
||||||
if (height<1) {
|
if (height<1) {
|
||||||
@@ -3875,9 +3907,14 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||||
|
if (size >= std::numeric_limits<int>::max() / nchannel || size <= 0) {
|
||||||
|
throw mjCError(this, "Builtin texture too large");
|
||||||
|
}
|
||||||
// allocate data
|
// allocate data
|
||||||
data.assign(nchannel*width*height, 0);
|
try {
|
||||||
if (data.empty()) {
|
data.assign(nchannel*size, 0);
|
||||||
|
} catch (const std::bad_alloc& e) {
|
||||||
throw mjCError(this, "Could not allocate memory for texture");
|
throw mjCError(this, "Could not allocate memory for texture");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -890,6 +890,52 @@ TEST_F(XMLReaderTest, MaterialTextureFailTest) {
|
|||||||
"cannot have texture sub-elements"));
|
"cannot have texture sub-elements"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(XMLReaderTest, LargeTextureTest) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<!--
|
||||||
|
Use a texture width that exceeds the maximum texture size. For cube
|
||||||
|
textures, the height is ignored and set to width*6. The default number of
|
||||||
|
channels is 3.
|
||||||
|
The width in this test is chosen so that 6*width*width*3 is too large to
|
||||||
|
be represented as an integer.
|
||||||
|
-->
|
||||||
|
<texture name="tex" builtin="gradient" width="10923" height="2"/>
|
||||||
|
</asset>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
|
||||||
|
EXPECT_THAT(model, IsNull());
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(XMLReaderTest, HugeTextureTest) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<!--
|
||||||
|
Use a texture width that exceeds the maximum texture size. For cube
|
||||||
|
textures, the height is ignored and set to width*6. The default number of
|
||||||
|
channels is 3.
|
||||||
|
The width in this test is chosen so that 6*width*width*3 is so large that
|
||||||
|
it overflows and becomes a positive integer.
|
||||||
|
-->
|
||||||
|
<texture name="tex" builtin="gradient" width="15447" height="2"/>
|
||||||
|
</asset>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
|
||||||
|
EXPECT_THAT(model, IsNull());
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, IncludeAssetsTest) {
|
TEST_F(XMLReaderTest, IncludeAssetsTest) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
|
|||||||
Reference in New Issue
Block a user