Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260805141033.841558-5-matthias.goergens@gmail.com>
Date: Wed,  5 Aug 2026 22:10:27 +0800
From: Matthias Goergens <matthias.goergens@...il.com>
To: musl@...ts.openwall.com
Cc: Matthias Goergens <matthias.goergens@...il.com>
Subject: [PATCH 04/10] regression: test allocated scanf buffer size overflow

On 32-bit targets %1073741823mlc wraps the wide-buffer allocation
to zero bytes unless the element count is checked. The conversion
must instead fail with ENOMEM before any allocation or store. The
test is a no-op where size_t is wider than 32 bits.

Covered by the musl patch "stdio: check allocated scanf buffer
sizes".
---
 src/regression/scanf-alloc-size-overflow.c | 102 +++++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 src/regression/scanf-alloc-size-overflow.c

diff --git a/src/regression/scanf-alloc-size-overflow.c b/src/regression/scanf-alloc-size-overflow.c
new file mode 100644
index 0000000..472fc11
--- /dev/null
+++ b/src/regression/scanf-alloc-size-overflow.c
@@ -0,0 +1,102 @@
+// The scanf allocation modifier computed wide-buffer byte sizes as
+// k * sizeof(wchar_t) without checking for size_t overflow. On 32-bit
+// targets %1073741823mlc wraps the allocation to zero bytes and the
+// first matching wide character is stored through the zero-size
+// object. The overlarge element count must be rejected with ENOMEM
+// before any allocation or store.
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <wchar.h>
+#include "test.h"
+
+#if ULONG_MAX == 0xffffffffUL
+
+static union {
+	long double align;
+	unsigned char bytes[64];
+} arena;
+static size_t requested;
+
+void *malloc(size_t size)
+{
+	requested = size;
+	return arena.bytes;
+}
+
+void free(void *ptr)
+{
+	(void)ptr;
+}
+
+void *realloc(void *ptr, size_t size)
+{
+	(void)ptr;
+	requested = size;
+	return arena.bytes;
+}
+
+static int canary_changed(void)
+{
+	size_t i;
+
+	for (i = 0; i < sizeof(wchar_t); i++)
+		if (arena.bytes[i] != 0xa5)
+			return 1;
+	return 0;
+}
+
+static void reset(void)
+{
+	size_t i;
+
+	for (i = 0; i < sizeof arena.bytes; i++)
+		arena.bytes[i] = 0xa5;
+	requested = SIZE_MAX;
+	errno = 0;
+}
+
+static void check(const char *what, int r, wchar_t *out)
+{
+	if (r != EOF)
+		t_error("%s returned %d, want EOF\n", what, r);
+	if (errno != ENOMEM)
+		t_error("%s left errno %d, want ENOMEM\n", what, errno);
+	if (requested != SIZE_MAX)
+		t_error("%s requested %zu bytes from the allocator, want none\n",
+			what, requested);
+	if (canary_changed())
+		t_error("%s stored through the overlarge buffer\n", what);
+	if (out)
+		t_error("%s stored a result pointer\n", what);
+}
+
+int main(void)
+{
+	wchar_t *out = 0;
+	int r;
+
+	reset();
+	r = sscanf("x", "%1073741823mlc", &out);
+	check("sscanf(\"x\", \"%%1073741823mlc\")", r, out);
+
+	out = 0;
+	reset();
+	r = swscanf(L"x", L"%1073741823mlc", &out);
+	check("swscanf(L\"x\", L\"%%1073741823mlc\")", r, out);
+
+	return t_status;
+}
+
+#else
+
+/* the widest scanf field width (INT_MAX wide characters) cannot
+   overflow the byte count when size_t is wider than 32 bits */
+int main(void)
+{
+	return t_status;
+}
+
+#endif
-- 
2.55.0

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.