Common: Validate the number of {} fields in format strings

Unfortunately, {fmt} allows passing too many arguments to a format call
without raising any runtime or compile-time error [1].

As this is a common source of bugs since we started migrating to {fmt},
this commit adds some custom logic to validate the number of
replacement fields in format strings in addition to {fmt}'s own checks.

[1] https://github.com/fmtlib/fmt/issues/492
This commit is contained in:
Léo Lam
2020-11-19 02:51:56 +01:00
parent 47c91696ee
commit 62eeb05519
5 changed files with 58 additions and 4 deletions

View File

@ -0,0 +1,41 @@
// Copyright 2020 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string_view>
namespace Common
{
constexpr std::size_t CountFmtReplacementFields(std::string_view s)
{
std::size_t count = 0;
for (std::size_t i = 0; i < s.size(); ++i)
{
if (s[i] != '{')
continue;
// If the opening brace is followed by another brace, what we have is
// an escaped brace, not a replacement field.
if (i + 1 < s.size() && s[i + 1] == '{')
{
// Skip the second brace.
// This ensures that e.g. {{{}}} is counted correctly: when the first brace character
// is read and detected as being part of an '{{' escape sequence, the second character
// is skipped so the most inner brace (the third character) is not detected
// as the end of an '{{' pair.
++i;
continue;
}
++count;
}
return count;
}
static_assert(CountFmtReplacementFields("") == 0);
static_assert(CountFmtReplacementFields("{} test {:x}") == 2);
static_assert(CountFmtReplacementFields("{} {{}} test {{{}}}") == 2);
} // namespace Common