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 <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
@@ -3423,6 +3425,9 @@ void mjCTexture::Builtin2D(void) {
|
||||
void mjCTexture::BuiltinCube(void) {
|
||||
unsigned char RGB1[3], RGB2[3], RGBm[3], RGBi[3];
|
||||
int w = width;
|
||||
if (w > std::numeric_limits<int>::max() / w) {
|
||||
throw mjCError(this, "Cube texture width is too large.");
|
||||
}
|
||||
int ww = width*width;
|
||||
|
||||
// convert fixed colors
|
||||
@@ -3436,6 +3441,9 @@ void mjCTexture::BuiltinCube(void) {
|
||||
|
||||
// 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 c = 0; c < w; c++) {
|
||||
// compute normalized pixel coordinates
|
||||
@@ -3677,12 +3685,17 @@ void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
|
||||
height = h;
|
||||
|
||||
// allocate and copy data
|
||||
data.assign(nchannel*width*height, 0);
|
||||
if (data.empty()) {
|
||||
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||
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)",
|
||||
(const char*)file_.c_str(), id);
|
||||
}
|
||||
memcpy(data.data(), image.data(), nchannel*width*height);
|
||||
memcpy(data.data(), image.data(), nchannel*size);
|
||||
image.clear();
|
||||
}
|
||||
|
||||
@@ -3712,12 +3725,20 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
|
||||
width = height = w;
|
||||
} else {
|
||||
width = w/gridsize[1];
|
||||
if (width >= std::numeric_limits<int>::max()/6) {
|
||||
throw mjCError(this, "Invalid width of cube texture");
|
||||
}
|
||||
height = 6*width;
|
||||
}
|
||||
|
||||
// allocate data
|
||||
data.assign(3*width*height, 0);
|
||||
if (data.empty()) {
|
||||
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||
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,
|
||||
"Could not allocate memory for texture '%s' (id %d)",
|
||||
(const char*)file_.c_str(), id);
|
||||
@@ -3816,9 +3837,17 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
||||
// first file: set size and allocate data
|
||||
if (data.empty()) {
|
||||
width = w;
|
||||
if (width >= std::numeric_limits<int>::max()/6) {
|
||||
throw mjCError(this, "Invalid width of builtin texture");
|
||||
}
|
||||
height = 6*width;
|
||||
data.assign(3*width*height, 0);
|
||||
if (data.empty()) {
|
||||
std::int64_t size = static_cast<std::int64_t>(width)*height;
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -3868,6 +3897,9 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
||||
|
||||
// adjust height of cube texture
|
||||
if (type != mjTEXTURE_2D) {
|
||||
if (width >= std::numeric_limits<int>::max()/6) {
|
||||
throw mjCError(this, "Invalid width of builtin texture");
|
||||
}
|
||||
height = 6*width;
|
||||
} else {
|
||||
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
|
||||
data.assign(nchannel*width*height, 0);
|
||||
if (data.empty()) {
|
||||
try {
|
||||
data.assign(nchannel*size, 0);
|
||||
} catch (const std::bad_alloc& e) {
|
||||
throw mjCError(this, "Could not allocate memory for texture");
|
||||
}
|
||||
|
||||
|
||||
@@ -890,6 +890,52 @@ TEST_F(XMLReaderTest, MaterialTextureFailTest) {
|
||||
"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) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
|
||||
Reference in New Issue
Block a user