From 96d3319d6f024b17ac725d9595548acc4787003c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 20 Sep 2021 21:19:25 +0000 Subject: [PATCH] Sema: relax va_start checking further for Windows AArch64 When building in C mode, the VC runtime assumes that it can use pointer aliasing through `char *` for the parameter to `__va_start`. Relax the checks further. In theory we could keep the tests strict for non-system header code, but this takes the less strict approach as the additional check doesn't particularly end up being too much more helpful for correctness. The C++ type system is a bit stricter and requires the explicit cast which we continue to verify. --- clang/lib/Sema/SemaChecking.cpp | 18 +++++- clang/test/Sema/microsoft-varargs.c | 97 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 clang/test/Sema/microsoft-varargs.c diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 99cd2b2278f1..0c47fb040d60 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -6413,6 +6413,21 @@ bool Sema::SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { } bool Sema::SemaBuiltinVAStartARMMicrosoft(CallExpr *Call) { + auto IsSuitablyTypedFormatArgument = [this](const Expr *Arg) -> bool { + const LangOptions &LO = getLangOpts(); + + if (LO.CPlusPlus) + return Arg->getType() + .getCanonicalType() + .getTypePtr() + ->getPointeeType() + .withoutLocalFastQualifiers() == Context.CharTy; + + // In C, allow aliasing through `char *`, this is required for AArch64 at + // least. + return true; + }; + // void __va_start(va_list *ap, const char *named_addr, size_t slot_size, // const char *named_addr); @@ -6441,8 +6456,7 @@ bool Sema::SemaBuiltinVAStartARMMicrosoft(CallExpr *Call) { const QualType &ConstCharPtrTy = Context.getPointerType(Context.CharTy.withConst()); - if (!Arg1Ty->isPointerType() || - Arg1Ty->getPointeeType().withoutLocalFastQualifiers() != Context.CharTy) + if (!Arg1Ty->isPointerType() || !IsSuitablyTypedFormatArgument(Arg1)) Diag(Arg1->getBeginLoc(), diag::err_typecheck_convert_incompatible) << Arg1->getType() << ConstCharPtrTy << 1 /* different class */ << 0 /* qualifier difference */ diff --git a/clang/test/Sema/microsoft-varargs.c b/clang/test/Sema/microsoft-varargs.c new file mode 100644 index 000000000000..979875e8cbae --- /dev/null +++ b/clang/test/Sema/microsoft-varargs.c @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -triple thumbv7-unknown-windows-msvc -fsyntax-only -x c %s -verify +// RUN: %clang_cc1 -triple aarch64-unknown-windows-msvc -fsyntax-only -x c %s -verify +// RUN: %clang_cc1 -triple thumbv7-unknown-windows-msvc -fsyntax-only -x c++ %s -verify +// RUN: %clang_cc1 -triple aarch64-unknown-windows-msvc -fsyntax-only -x c++ %s -verify +// expected-no-diagnostics + +#if defined _NO_CRT_STDIO_INLINE +# undef _CRT_STDIO_INLINE +# define _CRT_STDIO_INLINE +#elif !defined _CRT_STDIO_INLINE +# define _CRT_STDIO_INLINE __inline +#endif + +#ifndef _VA_LIST_DEFINED +#define _VA_LIST_DEFINED +typedef char *va_list; +#endif + +#if !defined __cplusplus +// Workaround for /Zc:wchar_t +typedef __WCHAR_TYPE__ wchar_t; +#endif + +#if defined __cplusplus +# define _ADDRESSOF(v) (&const_cast(reinterpret_cast(v))) +#else +# define _ADDRESSOF(v) (&(v)) +#endif + +#if defined _M_ARM +# define _VA_ALIGN 4 +# define _SLOTSIZEOF(t) ((sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1)) +# define _APALIGN(t,ap) (((va_list)0 - (ap)) & (__alignof(t) - 1)) +#elif defined _M_ARM64 +# define _VA_ALIGN 8 +# define _SLOTSIZEOF(t) ((sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1)) +# define _APALIGN(t,ap) (((va_list)0 - (ap)) & (__alignof(t) - 1)) +#endif + +#if defined _M_ARM +void __cdecl __va_start(va_list*, ...); +# if defined __cplusplus +# define __crt_va_start_a(ap, v) ((void)(__va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), _ADDRESSOF(v)))) +# else +# define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _SLOTSIZEOF(v))) +# endif + +# define __crt_va_arg(ap, t) (*(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t))) +# define __crt_va_end(ap) ((void)(ap = (va_list)0)) +#elif defined _M_ARM64 +void __cdecl __va_start(va_list*, ...); +# define __crt_va_start_a(ap,v) ((void)(__va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), __alignof(v), _ADDRESSOF(v)))) +# define __crt_va_arg(ap, t) \ + ((sizeof(t) > (2 * sizeof(__int64))) \ + ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ + : *(t*)((ap ++ _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t))) +# define __crt_va_end(ap) ((void)(ap = (va_list)0)) +#endif + +#if defined __cplusplus +extern "C++" { +template +struct __vcrt_va_list_is_reference { + enum : bool { __the_value = false }; +}; + +template +struct __vcrt_va_list_is_reference<_T&> { + enum : bool { __the_value = true }; +}; + +template +struct __vcrt_va_list_is_reference<_T&&> { + enum : bool { __the_value = true }; +}; + +template +struct __vcrt_assert_va_start_is_not_reference { + static_assert(!__vcrt_va_list_is_reference<_T>::__the_value, + "va_start argument must not have reference type and must not be parenthesized"); +}; +} + +# define __crt_va_start(ap, x) ((void)(__vcrt_assert_va_start_is_not_reference(), __crt_va_start_a(ap, x))) +#else +# define __crt_va_start(ap, x) __crt_va_start_a(ap, x) +#endif + +/*_Check_return_opt_*/ _CRT_STDIO_INLINE int __cdecl +wprintf(/*_In_z_ _Printf_format_string_*/ wchar_t const * const _Format, ...) { + int _Result; + va_list _ArgList; + __crt_va_start(_ArgList, _Format); + // _Result = _vfwprintf_l(stdout, _Format, NULL, _ArgList); + __crt_va_end(_ArgList); + return _Result; +}