summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Bindels <dascandy@gmail.com>2021-07-16 22:01:35 +0200
committerGunnar Beutner <gunnar@beutner.name>2021-07-17 00:43:34 +0200
commit85c2ad94d513fece80a76f822aca31fe45e6c51d (patch)
treead7dc5f2c477bf6c4a30caa8920370501d163646
parent3cca9e670434cf4001012d3d41935df8f50604c5 (diff)
downloadserenity-85c2ad94d513fece80a76f822aca31fe45e6c51d.zip
LibC: Make scanf always copy its va_list
On x86_64 GCC implements va_list as an array. This makes the syntax for taking a pointer to it break & crash. The workaround / solution is to create a copy. Since va_list is a tiny struct referencing the actual varargs, this is little overhead (especially compared to va_args itself)
-rw-r--r--Userland/Libraries/LibC/scanf.cpp6
1 files changed, 5 insertions, 1 deletions
diff --git a/Userland/Libraries/LibC/scanf.cpp b/Userland/Libraries/LibC/scanf.cpp
index ccdd63870c..cf46014eca 100644
--- a/Userland/Libraries/LibC/scanf.cpp
+++ b/Userland/Libraries/LibC/scanf.cpp
@@ -383,6 +383,9 @@ extern "C" int vsscanf(const char* input, const char* format, va_list ap)
int elements_matched = 0;
+ va_list copy;
+ __builtin_va_copy(copy, ap);
+
while (!format_lexer.is_eof()) {
format_lexer.ignore_while(isspace);
if (!format_lexer.next_is('%')) {
@@ -537,7 +540,7 @@ extern "C" int vsscanf(const char* input, const char* format, va_list ap)
}
}
- auto* ap_or_null = !suppress_assignment ? (va_list*)&ap : nullptr;
+ auto* ap_or_null = !suppress_assignment ? (va_list*)&copy : nullptr;
// Now try to read.
switch (conversion_specifier) {
@@ -616,6 +619,7 @@ extern "C" int vsscanf(const char* input, const char* format, va_list ap)
}
}
}
+ va_end(copy);
return elements_matched;
}