Commit 46c4979e authored by Ben Noordhuis's avatar Ben Noordhuis Committed by Commit Bot

Use wider types for max_old_space_size and co.

Make --max_old_space_size and friends work with values >= 2**31.
Such values did not work reliably (or sometimes not all) due to
signed integer overflow in size computations, which is UB.

Fixes https://github.com/nodejs/node/issues/18786.

Bug: chromium:814138
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Ibe23cef2417fd5b4a727022b8b0d4b50f1417182
Reviewed-on: https://chromium-review.googlesource.com/927063
Commit-Queue: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51433}
parent 4724d0ff
...@@ -6087,13 +6087,13 @@ class V8_EXPORT ResourceConstraints { ...@@ -6087,13 +6087,13 @@ class V8_EXPORT ResourceConstraints {
// Returns the max semi-space size in MB. // Returns the max semi-space size in MB.
V8_DEPRECATE_SOON("Use max_semi_space_size_in_kb()", V8_DEPRECATE_SOON("Use max_semi_space_size_in_kb()",
int max_semi_space_size()) { size_t max_semi_space_size()) {
return static_cast<int>(max_semi_space_size_in_kb_ / 1024); return max_semi_space_size_in_kb_ / 1024;
} }
// Sets the max semi-space size in MB. // Sets the max semi-space size in MB.
V8_DEPRECATE_SOON("Use set_max_semi_space_size_in_kb(size_t limit_in_kb)", V8_DEPRECATE_SOON("Use set_max_semi_space_size_in_kb(size_t limit_in_kb)",
void set_max_semi_space_size(int limit_in_mb)) { void set_max_semi_space_size(size_t limit_in_mb)) {
max_semi_space_size_in_kb_ = limit_in_mb * 1024; max_semi_space_size_in_kb_ = limit_in_mb * 1024;
} }
...@@ -6107,16 +6107,16 @@ class V8_EXPORT ResourceConstraints { ...@@ -6107,16 +6107,16 @@ class V8_EXPORT ResourceConstraints {
max_semi_space_size_in_kb_ = limit_in_kb; max_semi_space_size_in_kb_ = limit_in_kb;
} }
int max_old_space_size() const { return max_old_space_size_; } size_t max_old_space_size() const { return max_old_space_size_; }
void set_max_old_space_size(int limit_in_mb) { void set_max_old_space_size(size_t limit_in_mb) {
max_old_space_size_ = limit_in_mb; max_old_space_size_ = limit_in_mb;
} }
V8_DEPRECATE_SOON("max_executable_size_ is subsumed by max_old_space_size_", V8_DEPRECATE_SOON("max_executable_size_ is subsumed by max_old_space_size_",
int max_executable_size() const) { size_t max_executable_size() const) {
return max_executable_size_; return max_executable_size_;
} }
V8_DEPRECATE_SOON("max_executable_size_ is subsumed by max_old_space_size_", V8_DEPRECATE_SOON("max_executable_size_ is subsumed by max_old_space_size_",
void set_max_executable_size(int limit_in_mb)) { void set_max_executable_size(size_t limit_in_mb)) {
max_executable_size_ = limit_in_mb; max_executable_size_ = limit_in_mb;
} }
uint32_t* stack_limit() const { return stack_limit_; } uint32_t* stack_limit() const { return stack_limit_; }
...@@ -6127,17 +6127,15 @@ class V8_EXPORT ResourceConstraints { ...@@ -6127,17 +6127,15 @@ class V8_EXPORT ResourceConstraints {
code_range_size_ = limit_in_mb; code_range_size_ = limit_in_mb;
} }
size_t max_zone_pool_size() const { return max_zone_pool_size_; } size_t max_zone_pool_size() const { return max_zone_pool_size_; }
void set_max_zone_pool_size(const size_t bytes) { void set_max_zone_pool_size(size_t bytes) { max_zone_pool_size_ = bytes; }
max_zone_pool_size_ = bytes;
}
private: private:
// max_semi_space_size_ is in KB // max_semi_space_size_ is in KB
size_t max_semi_space_size_in_kb_; size_t max_semi_space_size_in_kb_;
// The remaining limits are in MB // The remaining limits are in MB
int max_old_space_size_; size_t max_old_space_size_;
int max_executable_size_; size_t max_executable_size_;
uint32_t* stack_limit_; uint32_t* stack_limit_;
size_t code_range_size_; size_t code_range_size_;
size_t max_zone_pool_size_; size_t max_zone_pool_size_;
......
...@@ -934,8 +934,7 @@ void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory, ...@@ -934,8 +934,7 @@ void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory,
uint64_t virtual_memory_limit) { uint64_t virtual_memory_limit) {
set_max_semi_space_size_in_kb( set_max_semi_space_size_in_kb(
i::Heap::ComputeMaxSemiSpaceSize(physical_memory)); i::Heap::ComputeMaxSemiSpaceSize(physical_memory));
set_max_old_space_size( set_max_old_space_size(i::Heap::ComputeMaxOldGenerationSize(physical_memory));
static_cast<int>(i::Heap::ComputeMaxOldGenerationSize(physical_memory)));
set_max_zone_pool_size(i::AccountingAllocator::kMaxPoolSize); set_max_zone_pool_size(i::AccountingAllocator::kMaxPoolSize);
if (virtual_memory_limit > 0 && i::kRequiresCodeRange) { if (virtual_memory_limit > 0 && i::kRequiresCodeRange) {
...@@ -950,7 +949,7 @@ void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory, ...@@ -950,7 +949,7 @@ void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory,
void SetResourceConstraints(i::Isolate* isolate, void SetResourceConstraints(i::Isolate* isolate,
const ResourceConstraints& constraints) { const ResourceConstraints& constraints) {
size_t semi_space_size = constraints.max_semi_space_size_in_kb(); size_t semi_space_size = constraints.max_semi_space_size_in_kb();
int old_space_size = constraints.max_old_space_size(); size_t old_space_size = constraints.max_old_space_size();
size_t code_range_size = constraints.code_range_size(); size_t code_range_size = constraints.code_range_size();
size_t max_pool_size = constraints.max_zone_pool_size(); size_t max_pool_size = constraints.max_zone_pool_size();
if (semi_space_size != 0 || old_space_size != 0 || code_range_size != 0) { if (semi_space_size != 0 || old_space_size != 0 || code_range_size != 0) {
......
...@@ -163,6 +163,7 @@ struct MaybeBoolFlag { ...@@ -163,6 +163,7 @@ struct MaybeBoolFlag {
#define DEFINE_INT(nam, def, cmt) FLAG(INT, int, nam, def, cmt) #define DEFINE_INT(nam, def, cmt) FLAG(INT, int, nam, def, cmt)
#define DEFINE_UINT(nam, def, cmt) FLAG(UINT, unsigned int, nam, def, cmt) #define DEFINE_UINT(nam, def, cmt) FLAG(UINT, unsigned int, nam, def, cmt)
#define DEFINE_FLOAT(nam, def, cmt) FLAG(FLOAT, double, nam, def, cmt) #define DEFINE_FLOAT(nam, def, cmt) FLAG(FLOAT, double, nam, def, cmt)
#define DEFINE_SIZE_T(nam, def, cmt) FLAG(SIZE_T, size_t, nam, def, cmt)
#define DEFINE_STRING(nam, def, cmt) FLAG(STRING, const char*, nam, def, cmt) #define DEFINE_STRING(nam, def, cmt) FLAG(STRING, const char*, nam, def, cmt)
#define DEFINE_ARGS(nam, cmt) \ #define DEFINE_ARGS(nam, cmt) \
FLAG(ARGS, JSArguments, nam, {0 COMMA nullptr}, cmt) FLAG(ARGS, JSArguments, nam, {0 COMMA nullptr}, cmt)
...@@ -170,6 +171,7 @@ struct MaybeBoolFlag { ...@@ -170,6 +171,7 @@ struct MaybeBoolFlag {
#define DEFINE_ALIAS_BOOL(alias, nam) FLAG_ALIAS(BOOL, bool, alias, nam) #define DEFINE_ALIAS_BOOL(alias, nam) FLAG_ALIAS(BOOL, bool, alias, nam)
#define DEFINE_ALIAS_INT(alias, nam) FLAG_ALIAS(INT, int, alias, nam) #define DEFINE_ALIAS_INT(alias, nam) FLAG_ALIAS(INT, int, alias, nam)
#define DEFINE_ALIAS_FLOAT(alias, nam) FLAG_ALIAS(FLOAT, double, alias, nam) #define DEFINE_ALIAS_FLOAT(alias, nam) FLAG_ALIAS(FLOAT, double, alias, nam)
#define DEFINE_ALIAS_SIZE_T(alias, nam) FLAG_ALIAS(SIZE_T, size_t, alias, nam)
#define DEFINE_ALIAS_STRING(alias, nam) \ #define DEFINE_ALIAS_STRING(alias, nam) \
FLAG_ALIAS(STRING, const char*, alias, nam) FLAG_ALIAS(STRING, const char*, alias, nam)
#define DEFINE_ALIAS_ARGS(alias, nam) FLAG_ALIAS(ARGS, JSArguments, alias, nam) #define DEFINE_ALIAS_ARGS(alias, nam) FLAG_ALIAS(ARGS, JSArguments, alias, nam)
...@@ -622,18 +624,18 @@ DEFINE_INT(stress_sampling_allocation_profiler, 0, ...@@ -622,18 +624,18 @@ DEFINE_INT(stress_sampling_allocation_profiler, 0,
"Enables sampling allocation profiler with X as a sample interval") "Enables sampling allocation profiler with X as a sample interval")
// Garbage collections flags. // Garbage collections flags.
DEFINE_INT(min_semi_space_size, 0, DEFINE_SIZE_T(min_semi_space_size, 0,
"min size of a semi-space (in MBytes), the new space consists of two" "min size of a semi-space (in MBytes), the new space consists of "
"semi-spaces") "two semi-spaces")
DEFINE_INT(max_semi_space_size, 0, DEFINE_SIZE_T(max_semi_space_size, 0,
"max size of a semi-space (in MBytes), the new space consists of two" "max size of a semi-space (in MBytes), the new space consists of "
"semi-spaces") "two semi-spaces")
DEFINE_INT(semi_space_growth_factor, 2, "factor by which to grow the new space") DEFINE_INT(semi_space_growth_factor, 2, "factor by which to grow the new space")
DEFINE_BOOL(experimental_new_space_growth_heuristic, false, DEFINE_BOOL(experimental_new_space_growth_heuristic, false,
"Grow the new space based on the percentage of survivors instead " "Grow the new space based on the percentage of survivors instead "
"of their absolute value.") "of their absolute value.")
DEFINE_INT(max_old_space_size, 0, "max size of the old space (in Mbytes)") DEFINE_SIZE_T(max_old_space_size, 0, "max size of the old space (in Mbytes)")
DEFINE_INT(initial_old_space_size, 0, "initial old space size (in Mbytes)") DEFINE_SIZE_T(initial_old_space_size, 0, "initial old space size (in Mbytes)")
DEFINE_BOOL(gc_global, false, "always perform global GCs") DEFINE_BOOL(gc_global, false, "always perform global GCs")
DEFINE_INT(random_gc_interval, 0, DEFINE_INT(random_gc_interval, 0,
"Collect garbage after random(0, X) allocations. It overrides " "Collect garbage after random(0, X) allocations. It overrides "
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "src/flags.h" #include "src/flags.h"
#include <cctype> #include <cctype>
#include <cerrno>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
...@@ -39,6 +40,7 @@ struct Flag { ...@@ -39,6 +40,7 @@ struct Flag {
TYPE_INT, TYPE_INT,
TYPE_UINT, TYPE_UINT,
TYPE_FLOAT, TYPE_FLOAT,
TYPE_SIZE_T,
TYPE_STRING, TYPE_STRING,
TYPE_ARGS TYPE_ARGS
}; };
...@@ -81,6 +83,11 @@ struct Flag { ...@@ -81,6 +83,11 @@ struct Flag {
return reinterpret_cast<double*>(valptr_); return reinterpret_cast<double*>(valptr_);
} }
size_t* size_t_variable() const {
DCHECK(type_ == TYPE_SIZE_T);
return reinterpret_cast<size_t*>(valptr_);
}
const char* string_value() const { const char* string_value() const {
DCHECK(type_ == TYPE_STRING); DCHECK(type_ == TYPE_STRING);
return *reinterpret_cast<const char**>(valptr_); return *reinterpret_cast<const char**>(valptr_);
...@@ -119,6 +126,11 @@ struct Flag { ...@@ -119,6 +126,11 @@ struct Flag {
return *reinterpret_cast<const double*>(defptr_); return *reinterpret_cast<const double*>(defptr_);
} }
size_t size_t_default() const {
DCHECK(type_ == TYPE_SIZE_T);
return *reinterpret_cast<const size_t*>(defptr_);
}
const char* string_default() const { const char* string_default() const {
DCHECK(type_ == TYPE_STRING); DCHECK(type_ == TYPE_STRING);
return *reinterpret_cast<const char* const *>(defptr_); return *reinterpret_cast<const char* const *>(defptr_);
...@@ -142,6 +154,8 @@ struct Flag { ...@@ -142,6 +154,8 @@ struct Flag {
return *uint_variable() == uint_default(); return *uint_variable() == uint_default();
case TYPE_FLOAT: case TYPE_FLOAT:
return *float_variable() == float_default(); return *float_variable() == float_default();
case TYPE_SIZE_T:
return *size_t_variable() == size_t_default();
case TYPE_STRING: { case TYPE_STRING: {
const char* str1 = string_value(); const char* str1 = string_value();
const char* str2 = string_default(); const char* str2 = string_default();
...@@ -173,6 +187,9 @@ struct Flag { ...@@ -173,6 +187,9 @@ struct Flag {
case TYPE_FLOAT: case TYPE_FLOAT:
*float_variable() = float_default(); *float_variable() = float_default();
break; break;
case TYPE_SIZE_T:
*size_t_variable() = size_t_default();
break;
case TYPE_STRING: case TYPE_STRING:
set_string_value(string_default(), false); set_string_value(string_default(), false);
break; break;
...@@ -201,6 +218,8 @@ static const char* Type2String(Flag::FlagType type) { ...@@ -201,6 +218,8 @@ static const char* Type2String(Flag::FlagType type) {
case Flag::TYPE_UINT: case Flag::TYPE_UINT:
return "uint"; return "uint";
case Flag::TYPE_FLOAT: return "float"; case Flag::TYPE_FLOAT: return "float";
case Flag::TYPE_SIZE_T:
return "size_t";
case Flag::TYPE_STRING: return "string"; case Flag::TYPE_STRING: return "string";
case Flag::TYPE_ARGS: return "arguments"; case Flag::TYPE_ARGS: return "arguments";
} }
...@@ -227,6 +246,9 @@ std::ostream& operator<<(std::ostream& os, const Flag& flag) { // NOLINT ...@@ -227,6 +246,9 @@ std::ostream& operator<<(std::ostream& os, const Flag& flag) { // NOLINT
case Flag::TYPE_FLOAT: case Flag::TYPE_FLOAT:
os << *flag.float_variable(); os << *flag.float_variable();
break; break;
case Flag::TYPE_SIZE_T:
os << *flag.size_t_variable();
break;
case Flag::TYPE_STRING: { case Flag::TYPE_STRING: {
const char* str = flag.string_value(); const char* str = flag.string_value();
os << (str ? str : "nullptr"); os << (str ? str : "nullptr");
...@@ -358,6 +380,27 @@ static Flag* FindFlag(const char* name) { ...@@ -358,6 +380,27 @@ static Flag* FindFlag(const char* name) {
return nullptr; return nullptr;
} }
template <typename T>
bool TryParseUnsigned(Flag* flag, const char* arg, const char* value,
char** endp, T* out_val) {
// We do not use strtoul because it accepts negative numbers.
// Rejects values >= 2**63 when T is 64 bits wide but that
// seems like an acceptable trade-off.
uint64_t max = static_cast<uint64_t>(std::numeric_limits<T>::max());
errno = 0;
int64_t val = static_cast<int64_t>(strtoll(value, endp, 10));
if (val < 0 || static_cast<uint64_t>(val) > max || errno != 0) {
PrintF(stderr,
"Error: Value for flag %s of type %s is out of bounds "
"[0-%" PRIu64
"]\n"
"Try --help for options\n",
arg, Type2String(flag->type()), max);
return false;
}
*out_val = static_cast<T>(val);
return true;
}
// static // static
int FlagList::SetFlagsFromCommandLine(int* argc, int FlagList::SetFlagsFromCommandLine(int* argc,
...@@ -422,27 +465,21 @@ int FlagList::SetFlagsFromCommandLine(int* argc, ...@@ -422,27 +465,21 @@ int FlagList::SetFlagsFromCommandLine(int* argc,
case Flag::TYPE_INT: case Flag::TYPE_INT:
*flag->int_variable() = static_cast<int>(strtol(value, &endp, 10)); *flag->int_variable() = static_cast<int>(strtol(value, &endp, 10));
break; break;
case Flag::TYPE_UINT: { case Flag::TYPE_UINT:
// We do not use strtoul because it accepts negative numbers. if (!TryParseUnsigned(flag, arg, value, &endp,
int64_t val = static_cast<int64_t>(strtoll(value, &endp, 10)); flag->uint_variable())) {
if (val < 0 || val > std::numeric_limits<unsigned int>::max()) {
PrintF(stderr,
"Error: Value for flag %s of type %s is out of bounds "
"[0-%" PRIu64
"]\n"
"Try --help for options\n",
arg, Type2String(flag->type()),
static_cast<uint64_t>(
std::numeric_limits<unsigned int>::max()));
return_code = j; return_code = j;
break;
} }
*flag->uint_variable() = static_cast<unsigned int>(val);
break; break;
}
case Flag::TYPE_FLOAT: case Flag::TYPE_FLOAT:
*flag->float_variable() = strtod(value, &endp); *flag->float_variable() = strtod(value, &endp);
break; break;
case Flag::TYPE_SIZE_T:
if (!TryParseUnsigned(flag, arg, value, &endp,
flag->size_t_variable())) {
return_code = j;
}
break;
case Flag::TYPE_STRING: case Flag::TYPE_STRING:
flag->set_string_value(value ? StrDup(value) : nullptr, true); flag->set_string_value(value ? StrDup(value) : nullptr, true);
break; break;
......
...@@ -5144,8 +5144,8 @@ bool Heap::ConfigureHeap(size_t max_semi_space_size_in_kb, ...@@ -5144,8 +5144,8 @@ bool Heap::ConfigureHeap(size_t max_semi_space_size_in_kb,
// The new space size must be a power of two to support single-bit testing // The new space size must be a power of two to support single-bit testing
// for containment. // for containment.
max_semi_space_size_ = base::bits::RoundUpToPowerOfTwo32( max_semi_space_size_ = static_cast<size_t>(base::bits::RoundUpToPowerOfTwo64(
static_cast<uint32_t>(max_semi_space_size_)); static_cast<uint64_t>(max_semi_space_size_)));
if (max_semi_space_size_ == kMaxSemiSpaceSizeInKB * KB) { if (max_semi_space_size_ == kMaxSemiSpaceSizeInKB * KB) {
// Start with at least 1*MB semi-space on machines with a lot of memory. // Start with at least 1*MB semi-space on machines with a lot of memory.
......
...@@ -612,15 +612,15 @@ class Heap { ...@@ -612,15 +612,15 @@ class Heap {
#endif #endif
// Semi-space size needs to be a multiple of page size. // Semi-space size needs to be a multiple of page size.
static const int kMinSemiSpaceSizeInKB = static const size_t kMinSemiSpaceSizeInKB =
1 * kPointerMultiplier * ((1 << kPageSizeBits) / KB); 1 * kPointerMultiplier * ((1 << kPageSizeBits) / KB);
static const int kMaxSemiSpaceSizeInKB = static const size_t kMaxSemiSpaceSizeInKB =
16 * kPointerMultiplier * ((1 << kPageSizeBits) / KB); 16 * kPointerMultiplier * ((1 << kPageSizeBits) / KB);
// The old space size has to be a multiple of Page::kPageSize. // The old space size has to be a multiple of Page::kPageSize.
// Sizes are in MB. // Sizes are in MB.
static const int kMinOldGenerationSize = 128 * kPointerMultiplier; static const size_t kMinOldGenerationSize = 128 * kPointerMultiplier;
static const int kMaxOldGenerationSize = 1024 * kPointerMultiplier; static const size_t kMaxOldGenerationSize = 1024 * kPointerMultiplier;
static const int kTraceRingBufferSize = 512; static const int kTraceRingBufferSize = 512;
static const int kStacktraceBufferSize = 512; static const int kStacktraceBufferSize = 512;
...@@ -1371,10 +1371,10 @@ class Heap { ...@@ -1371,10 +1371,10 @@ class Heap {
size_t MaxOldGenerationSize() { return max_old_generation_size_; } size_t MaxOldGenerationSize() { return max_old_generation_size_; }
static size_t ComputeMaxOldGenerationSize(uint64_t physical_memory) { static size_t ComputeMaxOldGenerationSize(uint64_t physical_memory) {
const int old_space_physical_memory_factor = 4; const size_t old_space_physical_memory_factor = 4;
int computed_size = size_t computed_size = static_cast<size_t>(
static_cast<int>(physical_memory / i::MB / physical_memory / i::MB / old_space_physical_memory_factor *
old_space_physical_memory_factor * kPointerMultiplier); kPointerMultiplier);
return Max(Min(computed_size, kMaxOldGenerationSize), return Max(Min(computed_size, kMaxOldGenerationSize),
kMinOldGenerationSize); kMinOldGenerationSize);
} }
...@@ -1386,8 +1386,8 @@ class Heap { ...@@ -1386,8 +1386,8 @@ class Heap {
uint64_t capped_physical_memory = uint64_t capped_physical_memory =
Max(Min(physical_memory, max_physical_memory), min_physical_memory); Max(Min(physical_memory, max_physical_memory), min_physical_memory);
// linearly scale max semi-space size: (X-A)/(B-A)*(D-C)+C // linearly scale max semi-space size: (X-A)/(B-A)*(D-C)+C
int semi_space_size_in_kb = size_t semi_space_size_in_kb =
static_cast<int>(((capped_physical_memory - min_physical_memory) * static_cast<size_t>(((capped_physical_memory - min_physical_memory) *
(kMaxSemiSpaceSizeInKB - kMinSemiSpaceSizeInKB)) / (kMaxSemiSpaceSizeInKB - kMinSemiSpaceSizeInKB)) /
(max_physical_memory - min_physical_memory) + (max_physical_memory - min_physical_memory) +
kMinSemiSpaceSizeInKB); kMinSemiSpaceSizeInKB);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment