arch-generic: Avoid out-of-memory errors for bad semihosting calls (#1143)
In BaseSemihosting::readString() we were using the len argument to allocate a std::vector without checking whether the value makes any sense. This resulted in a std::bad_alloc exception being raised prior to https://github.com/gem5/gem5/pull/1142 for my semihosting tests. This commit prevents semihosting from reading more than 64K for string arguments which should be more than sufficient for any valid code. Change-Id: I059669016ee2c5721fedb914595d0494f6cfd4cd
This commit is contained in:
committed by
GitHub
parent
adb177dab6
commit
6b34765d5d
@@ -157,9 +157,18 @@ BaseSemihosting::unserialize(CheckpointIn &cp)
|
|||||||
files[i] = FileBase::create(*this, cp, csprintf("file%i", i));
|
files[i] = FileBase::create(*this, cp, csprintf("file%i", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::optional<std::string>
|
||||||
BaseSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len)
|
BaseSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len)
|
||||||
{
|
{
|
||||||
|
if (len > 65536) {
|
||||||
|
// If the semihosting call passes an incorrect argument, reject it rather
|
||||||
|
// than attempting to allocate a buffer of that size. We chose 64K as
|
||||||
|
// an arbitrary limit here since no valid program should be attempting
|
||||||
|
// to open a file with such a large filename.
|
||||||
|
warn("BaseSemihosting::readString(): attempting to read too large "
|
||||||
|
"(%d bytes) string from %#x", len, ptr);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
std::vector<char> buf(len + 1);
|
std::vector<char> buf(len + 1);
|
||||||
|
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
@@ -179,7 +188,10 @@ BaseSemihosting::callOpen(
|
|||||||
if (!mode || !name_base)
|
if (!mode || !name_base)
|
||||||
return retError(EINVAL);
|
return retError(EINVAL);
|
||||||
|
|
||||||
std::string fname = readString(tc, name_base, name_size);
|
std::optional<std::string> fnameOpt = readString(tc, name_base, name_size);
|
||||||
|
if (!fnameOpt.has_value())
|
||||||
|
return retError(ERANGE);
|
||||||
|
std::string fname = *fnameOpt;
|
||||||
if (!fname.empty() && fname.front() != '/' && fname != ":tt" &&
|
if (!fname.empty() && fname.front() != '/' && fname != ":tt" &&
|
||||||
fname != ":semihosting-features")
|
fname != ":semihosting-features")
|
||||||
fname = filesRootDir + fname;
|
fname = filesRootDir + fname;
|
||||||
@@ -364,9 +376,11 @@ BaseSemihosting::RetErrno
|
|||||||
BaseSemihosting::callRemove(
|
BaseSemihosting::callRemove(
|
||||||
ThreadContext *tc, Addr name_base, size_t name_size)
|
ThreadContext *tc, Addr name_base, size_t name_size)
|
||||||
{
|
{
|
||||||
std::string fname = readString(tc, name_base, name_size);
|
std::optional<std::string> fname = readString(tc, name_base, name_size);
|
||||||
|
|
||||||
if (remove(fname.c_str()) != 0) {
|
if (!fname.has_value()) {
|
||||||
|
return retError(ERANGE);
|
||||||
|
} else if (remove(fname->c_str()) != 0) {
|
||||||
return retError(errno);
|
return retError(errno);
|
||||||
} else {
|
} else {
|
||||||
return retOK(0);
|
return retOK(0);
|
||||||
@@ -377,10 +391,11 @@ BaseSemihosting::RetErrno
|
|||||||
BaseSemihosting::callRename(ThreadContext *tc, Addr from_addr,
|
BaseSemihosting::callRename(ThreadContext *tc, Addr from_addr,
|
||||||
size_t from_size, Addr to_addr, size_t to_size)
|
size_t from_size, Addr to_addr, size_t to_size)
|
||||||
{
|
{
|
||||||
std::string from = readString(tc, from_addr, from_size);
|
std::optional<std::string> from = readString(tc, from_addr, from_size);
|
||||||
std::string to = readString(tc, to_addr, to_size);
|
std::optional<std::string> to = readString(tc, to_addr, to_size);
|
||||||
|
if (!from.has_value() || !to.has_value()) {
|
||||||
if (rename(from.c_str(), to.c_str()) != 0) {
|
return retError(ERANGE);
|
||||||
|
} else if (rename(from->c_str(), to->c_str()) != 0) {
|
||||||
return retError(errno);
|
return retError(errno);
|
||||||
} else {
|
} else {
|
||||||
return retOK(0);
|
return retOK(0);
|
||||||
@@ -402,9 +417,11 @@ BaseSemihosting::callTime(ThreadContext *tc)
|
|||||||
BaseSemihosting::RetErrno
|
BaseSemihosting::RetErrno
|
||||||
BaseSemihosting::callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size)
|
BaseSemihosting::callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size)
|
||||||
{
|
{
|
||||||
const std::string cmd = readString(tc, cmd_addr, cmd_size);
|
const std::optional<std::string> cmd = readString(tc, cmd_addr, cmd_size);
|
||||||
|
if (!cmd.has_value())
|
||||||
|
return retError(ERANGE);
|
||||||
warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
|
warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
|
||||||
cmd);
|
*cmd);
|
||||||
return retError(EINVAL);
|
return retError(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -381,7 +382,9 @@ class BaseSemihosting : public SimObject
|
|||||||
return tick >> tickShift;
|
return tick >> tickShift;
|
||||||
}
|
}
|
||||||
void semiExit(uint64_t code, uint64_t subcode);
|
void semiExit(uint64_t code, uint64_t subcode);
|
||||||
std::string readString(ThreadContext *tc, Addr ptr, size_t len);
|
|
||||||
|
std::optional<std::string> readString(ThreadContext *tc,
|
||||||
|
Addr ptr, size_t len);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::pair<uint64_t, SemiErrno> RetErrno;
|
typedef std::pair<uint64_t, SemiErrno> RetErrno;
|
||||||
|
|||||||
Reference in New Issue
Block a user