[clang] [clang][Sema] Fix over-read when scanf format ends with field width (PR #227716)
Atharva Ajmera via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 09:52:34 PDT 2026
https://github.com/atharvaajmera updated https://github.com/llvm/llvm-project/pull/227716
>From f9f5bc04354611fcc3ad2001e3af5100151c95d0 Mon Sep 17 00:00:00 2001
From: atharvaajmera <atharvaajmera06 at gmail.com>
Date: Wed, 30 Sep 2026 19:06:41 +0530
Subject: [PATCH 1/2] [clang][Sema] Fix over-read when scanf format ends with
field width
ParseAmount advances I to E when digits run to the end of the string
but returns NotSpecified, so ParseScanfSpecifier skipped its I == E
incomplete-specifier check for %*<width> at end-of-string. The parser
then read *E out-of-bounds via ParseLengthModifier, the NUL test, and
the conversion switch, producing bogus diagnostics and asserting in
StringLiteral::getLocationOfByte for concatenated literals.
Check I == E unconditionally after the field width, matching printf
and ParseArgPosition handling.
Fixes #227616
---
clang/docs/ReleaseNotes.md | 5 ++++-
clang/lib/AST/ScanfFormatString.cpp | 10 +++++-----
clang/test/Sema/format-strings-scanf.c | 19 +++++++++++++++++++
3 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f6..7296bf81c8f3ce 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -561,7 +561,10 @@ features cannot lower the translation-unit ABI level;
- Fixed a bug where a bit-field accessed as the result of a statement expression
(e.g. `({ s.b; })`) was not subject to integer promotion, unlike an ordinary
bit-field access. (#GH221542)
-
+- Fixed an assertion failure and bogus warnings when a `scanf` format string
+ ends with a field width such as `%*2`; it is now diagnosed as an incomplete
+ format specifier instead of reading past the end of the literal. (#GH227616)
+
#### Bug Fixes to Compiler Builtins
- Fixed a crash when classifying a call to a builtin with dependent arguments,
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index 5e1622e95277be..2030ee60251f7a 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -131,12 +131,12 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) {
assert(Amt.getHowSpecified() == OptionalAmount::Constant);
FS.setFieldWidth(Amt);
+ }
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompleteSpecifier(Start, E - Start);
+ return true;
}
// Look for the length modifier.
diff --git a/clang/test/Sema/format-strings-scanf.c b/clang/test/Sema/format-strings-scanf.c
index 941e3f7513bd6b..495c5b148d66c5 100644
--- a/clang/test/Sema/format-strings-scanf.c
+++ b/clang/test/Sema/format-strings-scanf.c
@@ -306,3 +306,22 @@ void test_promotion(void) {
scanf("%hhd", &c); // Pedantic warning?
scanf("%hhd", vp); // expected-warning{{format specifies type 'char *' but the argument has type 'void *'}}
}
+
+// GH227616: field width running to end-of-string must be diagnosed as
+// incomplete, not over-read past the literal.
+void test_incomplete_scanf_width(FILE *f, int *i, char *buf) {
+ fscanf(f, "%*2"); // expected-warning{{incomplete format specifier}}
+ fscanf(f, "%*12"); // expected-warning{{incomplete format specifier}}
+ fscanf(f, "a" "%*2", 0); // expected-warning{{incomplete format specifier}}
+ scanf("%*2"); // expected-warning{{incomplete format specifier}}
+ scanf("%2"); // expected-warning{{incomplete format specifier}}
+ scanf("%*"); // expected-warning{{incomplete format specifier}}
+ scanf("a" "%2"); // expected-warning{{incomplete format specifier}}
+
+ // Valid uses with a field width must not warn.
+ scanf("%2d", i); // no-warning
+ scanf("%*2d"); // no-warning
+ fscanf(f, "%*2d"); // no-warning
+ fscanf(f, "a" "%*2d"); // no-warning
+ sscanf(buf, "%*12d"); // no-warning
+}
>From dfac2099b8c6d397aea21605919a335a8bedb434 Mon Sep 17 00:00:00 2001
From: atharvaajmera <atharvaajmera06 at gmail.com>
Date: Thu, 1 Oct 2026 22:46:01 +0530
Subject: [PATCH 2/2] Address review nit: move comment before if
---
clang/lib/AST/ScanfFormatString.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index 2030ee60251f7a..6f2627c5390709 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -133,8 +133,8 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
FS.setFieldWidth(Amt);
}
+ // No more characters left.
if (I == E) {
- // No more characters left?
H.HandleIncompleteSpecifier(Start, E - Start);
return true;
}
More information about the cfe-commits
mailing list