iTranslated by AI
The Option to Intentionally Leave a Bug as a Bug
Introduction
In GCC v12.1, a patch (hereafter referred to as the "improvement patch") was incorporated into the C++ regex library std::regex to improve regex validation. Because of this improvement patch, invalid regex strings that previously bypassed validation are now "correctly" recognized as invalid, causing an exception to be thrown.
While this may sound like a purely positive change, that is not necessarily the case. Experienced developers might have felt a chill down their spine the moment they saw this. In this article, I will introduce the problems that can arise from this seemingly harmless improvement patch along with specific examples, and share the insight that deciding whether or not to apply such a patch can be a difficult judgment call.
Conclusion
- Problems caused by the improvement patch
- Conditions for occurrence
- Building a C++ program using GCC v12.1 or later, or any version where the improvement patch has been backported.
- Using C++
std::regex. - Having code that provides regex strings that are invalid from the perspective of
std::regex, such as "specifying a character class like\wbetween[and](e.g.,[\w-a]) ".
- Problems that occur
- When an invalid regex is provided, an exception will now be thrown in places where nothing happened previously. If this exception is not handled, it can lead to program crashes.
- Conditions for occurrence
- Insights
- Fixing a bug can cause code that operated based on that bug to stop working, potentially causing other issues.
- For example, the distributed storage Ceph becomes non-functional as a cluster if version 17.2.2, which is affected by this issue, is used.
- Although the side using it incorrectly is technically at fault, there is sometimes the option to leave a bug as a bug to prevent even larger problems caused by the fix.
- Fixing a bug can cause code that operated based on that bug to stop working, potentially causing other issues.
Content of the improvement patch
When a user provides an invalid regex string, std::regex throws a regex_error exception. However, previously, an exception was not thrown even if an invalid string—such as "specifying a character class like \w between [ and ] (e.g., [\w-a])"—was provided. Applying the patch introduced in the previous section resolves this issue. The following is a quote from the commit message of this patch:
std::regex currently allows invalid bracket ranges such as [\w-a] which
are only allowed by ECMAScript when in web browser compatibility mode.
It should be an error, because the start of the range is a character
class, not a single character. The current implementation of
_Compiler::_M_expression_term does not provide a way to reject this,
because we only remember a previous character, not whether we just
processed a character class (or collating symbol etc.)
Decision on whether to apply such a fix
From here, I will discuss the decision of whether or not to fix a bug—which can occur in any program, not just GCC—using the GCC improvement patch discussed so far as a case study.
The improvement patch seems beyond question, but that's not actually the case. If you build a program that happened to work despite being provided with an invalid regex string (as mentioned in the previous section) using a version of GCC that includes the improvement patch, an exception will now occur when the problematic code is executed, even if the program's own code hasn't changed at all. If exception handling has been neglected, it will cause issues such as program crashes. One might say, "Isn't the caller at fault?" and that is true, but regardless, a program that had (accidentally) been working until now will stop working.
This is where difficult decisions must be made. Since GCC is used by a vast number of programs and it's difficult to grasp the full picture of who is using it, sometimes the choice is made not to apply an improvement patch—leaving a bug as a bug—to ensure that programs that were already working continue to do so[1]. In software like Linux, where the impact of a fix is extremely high, developers are often forced to make such choices. It sounds like a lot of work.
Note that I do not hold any negative feelings toward the GCC developers' decision to apply the improvement patch. This is because I don't know what information they had or the reasons that led them to apply it; as an outsider, I think it would be irresponsible to say, "They should have done this instead." The purpose of this article is simply to let everyone know about the general option that "in cases like this, a fix might not be applied."
Specific problems caused by the GCC improvement patch
Recently, Ceph released a new version, v17.2.2, to fix a critical vulnerability. While v17.2.2 was intended to be a minimal update to v17.2.1 containing only the necessary vulnerability fixes, immediately after the update, a critical daemon began crashing repeatedly in a place entirely unrelated to those fixes. This caused the entire Ceph cluster to stop functioning.
If you have a sharp intuition, you might have already realized what happened: that critical daemon was using an invalid regex string that now triggers an exception because of the improvement patch. It was a coincidence that Ceph v17.2.1 had been built with a version of GCC that did not have the improvement patch, while v17.2.2 was built with a GCC version[2] that included it. To fix this issue, v17.2.3 was released just a few days later.
As an aside, when asked why a version that prevents any cluster from starting was released, the response was that there was a problem with the QA process and that they would prevent such things from happening in the future.
Conclusion
I learned about the existence of the improvement patch because I happened to see many reports of clusters becoming non-functional after the release of Ceph v17.2.2. I decided to share this because I thought it would be an excellent case study for less experienced software engineers to understand that the option exists to "intentionally not fix a bug even when its existence is known." I hope that, as intended, as many people as possible will become aware of this.
Discussion
修正が本当に問題になる場合、既存の関数やクラスの破壊的変更の代わりに新しい関数やクラスを作ってそれで置き換え、もともとの関数やクラスを非推奨にするという方法があります。
今回の場合は、そもそも不正な正規表現を与えていなければ問題は起きないので、修正しても十分許容範囲にあると思います。