mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
Fix 32-bit truncation in the flagset u64 round-trip
Mission flags are saved to the mission file as the full 64-bit flagset value, but three issues prevented anything larger than 32 bits from being saved as intended: * `stuff_flagset` parsed the value with `stuff_long`, where `atol` clamps anything above `LONG_MAX` * `flagset::from_u64` cast the incoming value to `unsigned long`, truncating bits 32-63 * `flagset::to_u64` used `bitset::to_ulong` where it should use `bitset::to_ullong` Fix these issues and add `static_assert` in the flagset functions to defend against future issues. Also use the proper UINT64_T_ARG where it should be used. This was first triggered by `Limited_support_rearm_pool`, the first mission flag to occupy bit 31. Only mission flags are affected; the other flagset serialization paths were audited and are safe. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
68e9a30463
commit
ba7aa70794
7 files changed
+96
-17
No files matched your search
@@ -201,9 +201,17 @@ class flagset {
|
||||
bool any_set() const { return values.any(); }
|
||||
bool none_set() const { return values.none(); }
|
||||
|
||||
void from_u64(std::uint64_t num) { values = (unsigned long) num; }
|
||||
void from_u64(std::uint64_t num)
|
||||
{
|
||||
static_assert(SIZE <= 64, "from_u64 can only be used with flagsets of up to 64 flags");
|
||||
values = std::bitset<SIZE>(num);
|
||||
}
|
||||
|
||||
std::uint64_t to_u64() const { return (std::uint64_t) values.to_ulong(); }
|
||||
std::uint64_t to_u64() const
|
||||
{
|
||||
static_assert(SIZE <= 64, "to_u64 can only be used with flagsets of up to 64 flags");
|
||||
return static_cast<std::uint64_t>(values.to_ullong());
|
||||
}
|
||||
|
||||
size_t hash() const { return std::hash<std::bitset<SIZE>>()(values); }
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@ bool gr_opengl_openxr_test_capabilities() {
|
||||
}
|
||||
|
||||
if (requirements.minApiVersionSupported > XR_MAKE_VERSION(GLVersion.major, GLVersion.minor, 0)) {
|
||||
mprintf(("System doesn't meet OpenXR graphics requirements (min %" PRIu64 ", available %" PRIu64 ")!\n", requirements.minApiVersionSupported, static_cast<XrVersion>(XR_MAKE_VERSION(GLVersion.major, GLVersion.minor, 0))));
|
||||
mprintf(("System doesn't meet OpenXR graphics requirements (min " UINT64_T_ARG ", available " UINT64_T_ARG ")!\n", requirements.minApiVersionSupported, static_cast<XrVersion>(XR_MAKE_VERSION(GLVersion.major, GLVersion.minor, 0))));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ bool compareDevices(const PhysicalDeviceValues& left, const PhysicalDeviceValues
|
||||
|
||||
void printPhysicalDevice(const PhysicalDeviceValues& values)
|
||||
{
|
||||
mprintf((" Found %s (%d) of type %s. API version %d.%d.%d, Driver version %d.%d.%d. Scored as %" PRIu64 "\n",
|
||||
mprintf((" Found %s (%d) of type %s. API version %d.%d.%d, Driver version %d.%d.%d. Scored as " UINT64_T_ARG "\n",
|
||||
values.properties.deviceName.data(),
|
||||
values.properties.deviceID,
|
||||
to_string(values.properties.deviceType).c_str(),
|
||||
|
||||
@@ -2566,7 +2566,7 @@ int Fred_mission_save::save_mission_info()
|
||||
fout("\n+Flags:");
|
||||
}
|
||||
|
||||
fout(" %" PRIu64, The_mission.flags.to_u64());
|
||||
fout(" " UINT64_T_ARG, The_mission.flags.to_u64());
|
||||
|
||||
// maybe write out Nebula values
|
||||
if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
|
||||
|
||||
@@ -2989,6 +2989,79 @@ int stuff_long(long *l, bool optional)
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Stuff an unsigned 64-bit value pointed at by Mp.
|
||||
// Advances past integer characters.
|
||||
int stuff_uint64(std::uint64_t *l, bool optional)
|
||||
{
|
||||
char *str_start = Mp;
|
||||
|
||||
// since strtoull ignores white space anyway, might as well make it explicit
|
||||
ignore_white_space();
|
||||
|
||||
// this is a bit cumbersome
|
||||
size_t span;
|
||||
if (*Mp == '+')
|
||||
{
|
||||
span = strspn(Mp + 1, "0123456789");
|
||||
|
||||
// account for the sign symbol, but not if it's the only valid character
|
||||
if (span > 0)
|
||||
++span;
|
||||
}
|
||||
else
|
||||
span = strspn(Mp, "0123456789");
|
||||
|
||||
// don't call strtoull unless we found digits, because it will happily parse
|
||||
// (and wrap around) a negative number
|
||||
std::uint64_t result = (span > 0) ? strtoull(Mp, nullptr, 10) : 0;
|
||||
bool success = false, comma = false;
|
||||
int retval = 0;
|
||||
|
||||
// no number found?
|
||||
if (span == 0)
|
||||
{
|
||||
if (!optional)
|
||||
error_display(1, "Expected unsigned integer, found [%.32s].\n", next_tokens());
|
||||
}
|
||||
else
|
||||
{
|
||||
*l = result;
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (success)
|
||||
Mp += span;
|
||||
|
||||
// if an unexpected character is part of the number, warn about it
|
||||
if (success && unexpected_numeric_char(*Mp))
|
||||
{
|
||||
error_display(0, "Expected unsigned integer, found [%.32s].\n", next_tokens(true));
|
||||
// Rather than back up to str_start, do what retail did and continue
|
||||
// merrily parsing along at the next character. (Optional numbers
|
||||
// will still back up to str_start - c.f. a few lines down.)
|
||||
if (optional)
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (check_first_non_grayspace_char(Mp, ',', &Mp))
|
||||
comma = true;
|
||||
|
||||
if (optional && !success)
|
||||
Mp = str_start;
|
||||
|
||||
if (success)
|
||||
{
|
||||
retval = 2;
|
||||
diag_printf("Stuffed uint64: " UINT64_T_ARG "\n", *l);
|
||||
}
|
||||
else if (optional)
|
||||
retval = comma ? 1 : 0;
|
||||
else
|
||||
skip_token();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int stuff_float_optional(float *f)
|
||||
{
|
||||
return stuff_float(f, true);
|
||||
|
||||
@@ -164,6 +164,7 @@ extern bool check_first_non_grayspace_char(const char *str, char ch_to_look_for,
|
||||
extern int stuff_float(float *f, bool optional = false);
|
||||
extern int stuff_int(int *i, bool optional = false);
|
||||
extern int stuff_long(long *l, bool optional = false);
|
||||
extern int stuff_uint64(std::uint64_t *l, bool optional = false);
|
||||
extern void stuff_ubyte(ubyte *i);
|
||||
extern int stuff_int_optional(int *i);
|
||||
extern int stuff_float_optional(float *f);
|
||||
@@ -246,17 +247,14 @@ void parse_string_flag_list_special(Flagset& dest, const special_flag_def_list_n
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void stuff_flagset(T *dest) {
|
||||
long l = 0;
|
||||
stuff_long(&l);
|
||||
void stuff_flagset(T *dest)
|
||||
{
|
||||
std::uint64_t val = 0;
|
||||
stuff_uint64(&val);
|
||||
|
||||
if (l < 0) {
|
||||
error_display(0, "Expected flagset value but got negative value %lu!\n", l);
|
||||
l = 0;
|
||||
}
|
||||
dest->from_u64((std::uint64_t) l);
|
||||
dest->from_u64(val);
|
||||
|
||||
diag_printf("Stuffed flagset: %" PRIu64 "\n", dest->to_u64());
|
||||
diag_printf("Stuffed flagset: " UINT64_T_ARG "\n", dest->to_u64());
|
||||
}
|
||||
|
||||
extern size_t stuff_int_list(int *ilp, size_t max_ints, ParseLookupType lookup_type = ParseLookupType::RAW_INTEGER_TYPE, bool warn_on_lookup_failure = true);
|
||||
|
||||
@@ -2383,15 +2383,15 @@ void game_show_framerate()
|
||||
MEMORYSTATUSEX mem_stats;
|
||||
mem_stats.dwLength = sizeof(mem_stats);
|
||||
if (GlobalMemoryStatusEx(&mem_stats)) {
|
||||
sprintf(mem_buffer, "Physical Free: %" PRIu64 " / %" PRIu64 " Meg", mem_stats.ullAvailPhys / 1024 / 1024, mem_stats.ullTotalPhys / 1024 / 1024);
|
||||
sprintf(mem_buffer, "Physical Free: " UINT64_T_ARG " / " UINT64_T_ARG " Meg", mem_stats.ullAvailPhys / 1024 / 1024, mem_stats.ullTotalPhys / 1024 / 1024);
|
||||
gr_string(sx, sy, mem_buffer.c_str(), GR_RESIZE_NONE);
|
||||
sy += line_height;
|
||||
|
||||
sprintf(mem_buffer, "Pagefile Free: %" PRIu64 " / %" PRIu64 " Meg", mem_stats.ullAvailPageFile / 1024 / 1024, mem_stats.ullTotalPageFile / 1024 / 1024);
|
||||
sprintf(mem_buffer, "Pagefile Free: " UINT64_T_ARG " / " UINT64_T_ARG " Meg", mem_stats.ullAvailPageFile / 1024 / 1024, mem_stats.ullTotalPageFile / 1024 / 1024);
|
||||
gr_string(sx, sy, mem_buffer.c_str(), GR_RESIZE_NONE);
|
||||
sy += line_height;
|
||||
|
||||
sprintf(mem_buffer, "Virtual Free: %" PRIu64 " / %" PRIu64 " Meg", mem_stats.ullAvailVirtual / 1024 / 1024, mem_stats.ullTotalVirtual / 1024 / 1024);
|
||||
sprintf(mem_buffer, "Virtual Free: " UINT64_T_ARG " / " UINT64_T_ARG " Meg", mem_stats.ullAvailVirtual / 1024 / 1024, mem_stats.ullTotalVirtual / 1024 / 1024);
|
||||
gr_string(sx, sy, mem_buffer.c_str(), GR_RESIZE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user