git: 602e98dd35ea - main - stdio: Fix bug in integer-parsing FSM

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Fri, 08 Aug 2025 22:46:41 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=602e98dd35ea5041b800fb56a2b1ac34f6649310

commit 602e98dd35ea5041b800fb56a2b1ac34f6649310
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-08-08 22:17:19 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-08-08 22:46:03 +0000

    stdio: Fix bug in integer-parsing FSM
    
    If we encounter a zero in the havezero state, we should assume octal,
    just like we would if we encountered any other digit below 8.
    
    MFC after:      1 week
    PR:             288440
    Fixes:          d9dc1603d6e4 ("libc: Implement N2630.")
    Reviewed by:    mandree
    Differential Revision:  https://reviews.freebsd.org/D51832
---
 lib/libc/stdio/vfscanf.c            |  5 ++---
 lib/libc/stdio/vfwscanf.c           |  5 ++---
 lib/libc/tests/stdio/sscanf_test.c  | 25 +++++++++++++++++++++++++
 lib/libc/tests/stdio/swscanf_test.c | 25 +++++++++++++++++++++++++
 4 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/lib/libc/stdio/vfscanf.c b/lib/libc/stdio/vfscanf.c
index a678710e1ecb..89e9e843969f 100644
--- a/lib/libc/stdio/vfscanf.c
+++ b/lib/libc/stdio/vfscanf.c
@@ -318,10 +318,9 @@ parseint_fsm(int c, enum parseint_state *state, int *base)
 	case '0':
 		if (*state == begin || *state == havesign) {
 			*state = havezero;
-		} else {
-			*state = any;
+			return 1;
 		}
-		return 1;
+		/* FALL THROUGH */
 	case '1':
 	case '2':
 	case '3':
diff --git a/lib/libc/stdio/vfwscanf.c b/lib/libc/stdio/vfwscanf.c
index 57206a8407d5..7ca64eb37811 100644
--- a/lib/libc/stdio/vfwscanf.c
+++ b/lib/libc/stdio/vfwscanf.c
@@ -298,10 +298,9 @@ parseint_fsm(wchar_t c, enum parseint_state *state, int *base)
 	case '0':
 		if (*state == begin || *state == havesign) {
 			*state = havezero;
-		} else {
-			*state = any;
+			return 1;
 		}
-		return 1;
+		/* FALL THROUGH */
 	case '1':
 	case '2':
 	case '3':
diff --git a/lib/libc/tests/stdio/sscanf_test.c b/lib/libc/tests/stdio/sscanf_test.c
index e916873d38c3..e43292820eeb 100644
--- a/lib/libc/tests/stdio/sscanf_test.c
+++ b/lib/libc/tests/stdio/sscanf_test.c
@@ -68,6 +68,31 @@ static const struct sscanf_test_case {
 	{ "0e",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
 	{ "0f",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
 	{ "0x",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// all digits with two leading zeroes
+	{ "000",	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 }, },
+	{ "001",	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 }, },
+	{ "002",	{ 1,   0, 2 },	{ 1,   2, 3 },	{ 1,   2, 3 },	{ 1,   2, 3 },	{ 1,   2, 3 }, },
+	{ "003",	{ 1,   0, 2 },	{ 1,   3, 3 },	{ 1,   3, 3 },	{ 1,   3, 3 },	{ 1,   3, 3 }, },
+	{ "004",	{ 1,   0, 2 },	{ 1,   4, 3 },	{ 1,   4, 3 },	{ 1,   4, 3 },	{ 1,   4, 3 }, },
+	{ "005",	{ 1,   0, 2 },	{ 1,   5, 3 },	{ 1,   5, 3 },	{ 1,   5, 3 },	{ 1,   5, 3 }, },
+	{ "006",	{ 1,   0, 2 },	{ 1,   6, 3 },	{ 1,   6, 3 },	{ 1,   6, 3 },	{ 1,   6, 3 }, },
+	{ "007",	{ 1,   0, 2 },	{ 1,   7, 3 },	{ 1,   7, 3 },	{ 1,   7, 3 },	{ 1,   7, 3 }, },
+	{ "008",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   8, 3 },	{ 1,   8, 3 },	{ 1,   0, 2 }, },
+	{ "009",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   9, 3 },	{ 1,   9, 3 },	{ 1,   0, 2 }, },
+	{ "00A",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  10, 3 },	{ 1,   0, 2 }, },
+	{ "00B",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  11, 3 },	{ 1,   0, 2 }, },
+	{ "00C",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  12, 3 },	{ 1,   0, 2 }, },
+	{ "00D",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  13, 3 },	{ 1,   0, 2 }, },
+	{ "00E",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  14, 3 },	{ 1,   0, 2 }, },
+	{ "00F",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  15, 3 },	{ 1,   0, 2 }, },
+	{ "00X",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
+	{ "00a",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  10, 3 },	{ 1,   0, 2 }, },
+	{ "00b",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  11, 3 },	{ 1,   0, 2 }, },
+	{ "00c",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  12, 3 },	{ 1,   0, 2 }, },
+	{ "00d",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  13, 3 },	{ 1,   0, 2 }, },
+	{ "00e",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  14, 3 },	{ 1,   0, 2 }, },
+	{ "00f",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  15, 3 },	{ 1,   0, 2 }, },
+	{ "00x",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
 	// all digits with leading one
 	{ "10",		{ 1,   2, 2 },	{ 1,   8, 2 },	{ 1,  10, 2 },	{ 1,  16, 2 },	{ 1,  10, 2 }, },
 	{ "11",		{ 1,   3, 2 },	{ 1,   9, 2 },	{ 1,  11, 2 },	{ 1,  17, 2 },	{ 1,  11, 2 }, },
diff --git a/lib/libc/tests/stdio/swscanf_test.c b/lib/libc/tests/stdio/swscanf_test.c
index f7ad30b963a7..f0638081e16f 100644
--- a/lib/libc/tests/stdio/swscanf_test.c
+++ b/lib/libc/tests/stdio/swscanf_test.c
@@ -71,6 +71,31 @@ static const struct swscanf_test_case {
 	{ L"0e",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
 	{ L"0f",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
 	{ L"0x",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// all digits with two leading zeroes
+	{ L"000",	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 },	{ 1,   0, 3 }, },
+	{ L"001",	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 },	{ 1,   1, 3 }, },
+	{ L"002",	{ 1,   0, 2 },	{ 1,   2, 3 },	{ 1,   2, 3 },	{ 1,   2, 3 },	{ 1,   2, 3 }, },
+	{ L"003",	{ 1,   0, 2 },	{ 1,   3, 3 },	{ 1,   3, 3 },	{ 1,   3, 3 },	{ 1,   3, 3 }, },
+	{ L"004",	{ 1,   0, 2 },	{ 1,   4, 3 },	{ 1,   4, 3 },	{ 1,   4, 3 },	{ 1,   4, 3 }, },
+	{ L"005",	{ 1,   0, 2 },	{ 1,   5, 3 },	{ 1,   5, 3 },	{ 1,   5, 3 },	{ 1,   5, 3 }, },
+	{ L"006",	{ 1,   0, 2 },	{ 1,   6, 3 },	{ 1,   6, 3 },	{ 1,   6, 3 },	{ 1,   6, 3 }, },
+	{ L"007",	{ 1,   0, 2 },	{ 1,   7, 3 },	{ 1,   7, 3 },	{ 1,   7, 3 },	{ 1,   7, 3 }, },
+	{ L"008",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   8, 3 },	{ 1,   8, 3 },	{ 1,   0, 2 }, },
+	{ L"009",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   9, 3 },	{ 1,   9, 3 },	{ 1,   0, 2 }, },
+	{ L"00A",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  10, 3 },	{ 1,   0, 2 }, },
+	{ L"00B",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  11, 3 },	{ 1,   0, 2 }, },
+	{ L"00C",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  12, 3 },	{ 1,   0, 2 }, },
+	{ L"00D",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  13, 3 },	{ 1,   0, 2 }, },
+	{ L"00E",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  14, 3 },	{ 1,   0, 2 }, },
+	{ L"00F",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  15, 3 },	{ 1,   0, 2 }, },
+	{ L"00X",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
+	{ L"00a",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  10, 3 },	{ 1,   0, 2 }, },
+	{ L"00b",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  11, 3 },	{ 1,   0, 2 }, },
+	{ L"00c",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  12, 3 },	{ 1,   0, 2 }, },
+	{ L"00d",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  13, 3 },	{ 1,   0, 2 }, },
+	{ L"00e",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  14, 3 },	{ 1,   0, 2 }, },
+	{ L"00f",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,  15, 3 },	{ 1,   0, 2 }, },
+	{ L"00x",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
 	// all digits with leading one
 	{ L"10",	{ 1,   2, 2 },	{ 1,   8, 2 },	{ 1,  10, 2 },	{ 1,  16, 2 },	{ 1,  10, 2 }, },
 	{ L"11",	{ 1,   3, 2 },	{ 1,   9, 2 },	{ 1,  11, 2 },	{ 1,  17, 2 },	{ 1,  11, 2 }, },